Compare commits

...

62 Commits

Author SHA1 Message Date
Fabian Thomys
e971a9ebf5 Qt: Implement option for organizing screenshots in folders by game name 2025-07-24 18:10:11 +02:00
TheTechnician27
19f85713ce Settings: Transfer regional frame rate to advanced Graphics settings 2025-07-24 18:09:12 +02:00
Slayer0fRA
4c41de6013 Debugger: Disable filter search button while filter search is active 2025-07-23 22:53:16 -04:00
Slayer0fRA
96f9cde1bc Debugger: Disable filter search button while filter search is active 2025-07-23 22:53:16 -04:00
PCSX2 Bot
3feef0824c [ci skip] PAD: Update to latest controller database. 2025-07-23 03:07:03 +02:00
lightningterror
5f2ab55c93 GS/GL/MTL: Misc fixes.
MTL: Add missing shader case for RGB5A1_TO_8I.
GL: Comment out vendor Nvidia.

Fixes warnings.
2025-07-20 22:41:41 +02:00
GovanifY
0f75bfe17d Revert "GitHub: Add types to issue templates"
This reverts commit 7faa132622.
2025-07-20 12:46:38 +02:00
Berylskid
7faa132622 GitHub: Add types to issue templates 2025-07-19 15:28:51 -04:00
Ziemas
b69e6da105 Debugger: add 3 operand mult to assembler 2025-07-19 15:28:31 -04:00
JordanTheToaster
11edb128e9 GameDB: Kamen Rider Kabuto fixes 2025-07-19 20:25:57 +02:00
TJnotJT
fc4407aaef GS: Add secondary vertex buffer for copy/modifying vertices.
Currently only used in HW renderer to fix vertices for provoking-first-vertex APIs.
2025-07-19 02:39:01 +02:00
TJnotJT
2a418e3282 GS/HW: Handle first vertex provoking APIs in GSRendererHW.cpp. 2025-07-19 02:39:01 +02:00
TJnotJT
59415542ff GS: Remove all usage of provoking first/flat swapped in early pipeline.
Removes said usage from GSState and GSVertexTrace (and helper classes). The end goal is to support first-vertex-provoking APIs in GSRendererHW instead of early in the pipeline.
2025-07-19 02:39:01 +02:00
Jordan
00416e26bb GameDB: Various fixes (#13041)
Various fixes for incorrect names and missing fixes ect in Minority Report Gran Turismo 2000 Trial and fixing the name of Shaun White Snowboarding which was incorrectly labelled Rock Band 2 for some reason.
2025-07-19 00:26:29 +02:00
JordanTheToaster
e936398e17 Deps: Update SDL3 to v3.2.18 2025-07-17 12:24:12 +02:00
PCSX2 Bot
e3b45c3efd [ci skip] Qt: Update Base Translation. 2025-07-17 02:02:52 +02:00
JordanTheToaster
7ad759007a ImGuiOverlays: Internal FPS stat regression fix 2025-07-17 02:00:37 +02:00
refractionpcsx2
2fab554360 GS: Check tex rect of draw on overlap check if draw is single page wide. 2025-07-16 14:08:30 +02:00
refractionpcsx2
e98b6e6cb3 GameDB: Add Sand Grain Games CRC to known affected games 2025-07-16 14:08:30 +02:00
refractionpcsx2
aa5147a52b GS/HW: Add CRC for Sand Grain Games palette shuffle effect. 2025-07-16 14:08:30 +02:00
lightningterror
83ebbe95c5 GS/TC: Don't split/resize buffer when texture buffer width is 0.
Textures wrap within a single page.
2025-07-16 09:49:04 +02:00
Ziemas
2ad71d046b Debugger: Fix copying out of sorted thread view 2025-07-16 09:47:09 +02:00
lightningterror
099e1f900d GS/TC: Fix wrong block per page in LookupSource. 2025-07-15 16:04:58 +02:00
PCSX2 Bot
0c6537e917 [ci skip] PAD: Update to latest controller database. 2025-07-14 21:35:06 +02:00
JordanTheToaster
8937fb3d11 CI: Update macOS runners to 15 2025-07-13 09:22:22 -04:00
JordanTheToaster
028085f8f9 GameDB: El Tigre fixes 2025-07-13 14:43:16 +02:00
PCSX2 Bot
b33932f47b [ci skip] Qt: Update Base Translation. 2025-07-13 02:02:56 +02:00
TheLastRar
d122939183 UnitTests: Don't include ImGuiAnimated in stubs
This defines no Host functions
2025-07-12 20:10:59 +02:00
TheLastRar
b09eb4e42d VS: Fix ImGuiAnimated filter 2025-07-12 20:10:59 +02:00
TheLastRar
e75ddb878a FSUI: Disable the nav window using context config options 2025-07-12 20:10:59 +02:00
TheLastRar
6e3dca5a1a FSUI: Remove font glyph ranges and add exclude ranges
The original glyph ranges are legacy and aren't used
2025-07-12 20:10:59 +02:00
TheLastRar
b069f51e6f FSUI: Make use of scalable fonts 2025-07-12 20:10:59 +02:00
TheLastRar
bb1e6d06e4 FSUI: Support RendererHasTextures 2025-07-12 20:10:59 +02:00
TheLastRar
f747abcc3b GS: Prefix page/block size defines 2025-07-12 20:10:59 +02:00
TheLastRar
77738f72b4 3rdparty: Update ImGui to 1.92.1 2025-07-12 20:10:59 +02:00
JordanTheToaster
61f11d12ff 3rdparty: Update xbyak to v7.27 2025-07-12 19:53:10 +02:00
JordanTheToaster
7e00b2c9a9 3rdparty: Update WIL to v1.0.250325.1 2025-07-12 19:53:10 +02:00
JordanTheToaster
7dd4ab947b 3rdparty: Update googletest to v1.17.0 2025-07-12 19:53:10 +02:00
JordanTheToaster
be213695a0 3rdparty: Update CPUInfo to commit e4cadd02a8b386c38b84f0a19eddacec3f433baa 2025-07-12 19:53:10 +02:00
JordanTheToaster
cde548a2f9 3rdparty: Update libzip to 1.14 2025-07-12 19:53:10 +02:00
JordanTheToaster
e76481f454 Deps: Update libpng to v1.6.50 2025-07-12 19:53:10 +02:00
JordanTheToaster
1a8d1d443e Deps: Update libjpeg-turbo to v3.1.1
a
2025-07-12 19:53:10 +02:00
JordanTheToaster
a14c8eb7d5 3rdparty: Update LZMA/7zipSDK to 25.00 2025-07-12 19:53:10 +02:00
SternXD
97ea52a6c1 FSUI: Add memory card creation dialog 2025-07-12 19:50:53 +02:00
SternXD
0617182dfc Qt: Add option to start in Big Picture Mode from settings. 2025-07-12 19:50:22 +02:00
KamFretoZ
bd99d76416 Qt: Tidy up post processing section 2025-07-12 19:49:18 +02:00
KamFretoZ
fdbe9305a9 Qt: Move screenshot section to Recording tab
Qt: Move Video Dump Directory to Folder Settings
2025-07-12 19:49:18 +02:00
JordanTheToaster
fd772e42da CI/Linux: Switch appimage URL to new link 2025-07-11 21:49:08 +02:00
Fabian Thomys
df56797b38 Qt/Debugger: Remove text display for breakpoint enabled state 2025-07-10 20:40:20 -04:00
TellowKrinkle
28e72110bb GS: Remove Fermi vs expand disable
It works now.
2025-07-10 18:42:56 +02:00
TellowKrinkle
e451c5f70c GS: Remove use of shader_draw_parameters
Should fix older Nvidia GPUs
2025-07-10 18:42:56 +02:00
TellowKrinkle
20b8fef950 GS:VK: Allow running on GPUs without EXT_line_rasterization 2025-07-10 18:42:56 +02:00
SternXD
47449a424d GS: Fix GS Dump OSD text 2025-07-10 18:41:58 +02:00
SternXD
e8ab761be4 FSUI: Fix Deadlock in Achievements Tab 2025-07-10 18:41:58 +02:00
refractionpcsx2
6463fc541b GS: Improve triangle quad detection for strips 2025-07-10 18:37:25 +02:00
refractionpcsx2
08b3194b96 GS/HW: Improve temp z smaller copy detection 2025-07-10 18:37:25 +02:00
chaoticgd
62fa768e60 PINE: Give the server thread a name 2025-07-09 04:19:09 +02:00
chaoticgd
cafe7a9dc2 Debugger: Improve error handling in getIOPThreads and getIOPModules 2025-07-08 22:02:54 +02:00
chaoticgd
7ffc671c3a Memcard: Remove a buggy assertion 2025-07-08 21:27:17 +02:00
JordanTheToaster
eaeea83547 GraphicsSettingsWidget: Correctly set current index 2025-07-08 11:32:53 +02:00
SternXD
76b758dbd2 OSD/Achievements: Add 9-position alignment options for Achievement notifications/popups, and OSD 2025-07-08 10:35:40 +02:00
Haisom
ab19b109ce Core/Qt: Add hotkey support for swapping memory card
### Description of Changes

- Adds Swap Memory Card function.
- Adds Helper Function to verify whenever memory cards are auto-ejecting.
- Adds assert to make sure the memory cards swapping function will only RunOnCPUThreat.
- Adds field to set a custom hotkey under Controllers>Hotkeys to quickly swap memory cards.

### Rationale behind Changes

- Allow users to change memory cards on demand. This is really useful on shared machines, specially with kids around (Forget kids accidentally overwriting your save games with over 100 hours of gameplay!).
- This will easy up process for saving on backup memory cards on the fly without the need of auxiliary tools such as "mymc".
- By creating a memory card swap function in the core, we can now use it anywhere.

### Suggested Testing Steps

- Assign hotkey under Controllers>Hotkeys>Swap Memory Cards and test while on BIOS Browser or in game.

Special thanks to @kamfretoz  @RedDevilus , @RedPanda4552 , and @Mrlinkwii  for the feedback, suggestions and troubleshooting!

Co-Authored-By: pandubz <redpanda4552@gmail.com>
Co-Authored-By: KamFretoZ <14798312+kamfretoz@users.noreply.github.com>
2025-07-08 10:34:59 +02:00
193 changed files with 16094 additions and 9710 deletions

View File

@@ -12,7 +12,7 @@ on:
os:
required: false
type: string
default: macos-14
default: macos-15
patchesUrl:
required: false
type: string
@@ -62,8 +62,8 @@ jobs:
echo "#define DEFAULT_UPDATER_CHANNEL \"stable\"" > ./pcsx2-qt/DefaultUpdaterChannel.h
cat ./pcsx2-qt/DefaultUpdaterChannel.h
- name: Use Xcode 15.2
run: sudo xcode-select -s /Applications/Xcode_15.2.app
- name: Use Xcode 16.4
run: sudo xcode-select -s /Applications/Xcode_16.4.app
- name: Prepare Artifact Metadata
id: artifact-metadata

View File

@@ -9,6 +9,5 @@ set -e
"$SCRIPTDIR/../../../../tools/retry.sh" sudo apt-get -y install qt6-l10n-tools python3
"$SCRIPTDIR/../../../../tools/generate_fullscreen_ui_translation_strings.py"
"$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_glyph_ranges.py"
"$SCRIPTDIR/../../../../tools/generate_update_fa_glyph_ranges.py"
PATH=/usr/lib/qt6/bin:$PATH "$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_base_translation.sh"

View File

@@ -68,7 +68,7 @@ if [ ! -f "$LINUXDEPLOY_PLUGIN_QT" ]; then
fi
if [ ! -f "$APPIMAGETOOL" ]; then
"$PCSX2DIR/tools/retry.sh" wget -O "$APPIMAGETOOL" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
"$PCSX2DIR/tools/retry.sh" wget -O "$APPIMAGETOOL" https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x "$APPIMAGETOOL"
fi

View File

@@ -15,10 +15,10 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
fi
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.0
LIBPNG=1.6.48
LIBJPEGTURBO=3.1.1
LIBPNG=1.6.50
LIBWEBP=1.5.0
SDL=SDL3-3.2.16
SDL=SDL3-3.2.18
QT=6.9.1
LZ4=1.10.0
ZSTD=1.5.7
@@ -36,10 +36,10 @@ cd deps-build
cat > SHASUMS <<EOF
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
46fd06ff37db1db64c0dc288d78a3f5efd23ad9ac41561193f983e20937ece03 libpng-$LIBPNG.tar.xz
aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c libjpeg-turbo-$LIBJPEGTURBO.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c libwebp-$LIBWEBP.tar.gz
6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8 $SDL.tar.gz
1a775bde924397a8e0c08bfda198926c17be859d0288ad0dec1dea1b2ee04f8f $SDL.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
40caedbf83cc9a1959610830563565889878bc95f115868bbf545d1914acf28e qtbase-everywhere-src-$QT.tar.xz

View File

@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.2.16.tar.gz",
"sha256": "6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8"
"url": "https://libsdl.org/release/SDL3-3.2.18.tar.gz",
"sha256": "1a775bde924397a8e0c08bfda198926c17be859d0288ad0dec1dea1b2ee04f8f"
}
],
"cleanup": [

View File

@@ -40,11 +40,11 @@ fi
FREETYPE=2.13.3
HARFBUZZ=11.2.0
SDL=SDL3-3.2.16
SDL=SDL3-3.2.18
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.48
LIBJPEGTURBO=3.1.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.1
LIBWEBP=1.5.0
FFMPEG=6.0
MOLTENVK=1.2.9
@@ -79,12 +79,12 @@ CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8 $SDL.tar.gz
1a775bde924397a8e0c08bfda198926c17be859d0288ad0dec1dea1b2ee04f8f $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
46fd06ff37db1db64c0dc288d78a3f5efd23ad9ac41561193f983e20937ece03 libpng-$LIBPNG.tar.xz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c libwebp-$LIBWEBP.tar.gz
9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz

View File

@@ -22,11 +22,11 @@ fi
FREETYPE=2.13.3
HARFBUZZ=11.2.0
SDL=SDL3-3.2.16
SDL=SDL3-3.2.18
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.48
LIBJPEGTURBO=3.1.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.1
LIBWEBP=1.5.0
FFMPEG=6.0
MOLTENVK=1.2.9
@@ -59,12 +59,12 @@ CMAKE_COMMON=(
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8 $SDL.tar.gz
1a775bde924397a8e0c08bfda198926c17be859d0288ad0dec1dea1b2ee04f8f $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
46fd06ff37db1db64c0dc288d78a3f5efd23ad9ac41561193f983e20937ece03 libpng-$LIBPNG.tar.xz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c libwebp-$LIBWEBP.tar.gz
9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz

View File

@@ -44,9 +44,9 @@ cd "%BUILDDIR%"
set FREETYPE=2.13.3
set HARFBUZZ=11.2.0
set LIBJPEGTURBO=3.1.0
set LIBPNG=1648
set SDL=SDL3-3.2.16
set LIBJPEGTURBO=3.1.1
set LIBPNG=1650
set SDL=SDL3-3.2.18
set QT=6.9.1
set QTMINOR=6.9
set LZ4=1.10.0
@@ -65,10 +65,10 @@ set SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 850cb5e38e21106c0abba86c5b73f8f74b9a32d7725505901d081080b0d3f0b3 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1648.zip 2e5f080360f77376eb2bfa9e2ed773b9c7728159aba47b638ad53ca839379040 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1650.zip 4be6938313b08d5921f9dede13f2789b653c96f4f8595d92ff3f09c9320e51c7 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 0cc7430fb827c1f843e31b8b26ba7f083b1eeb8f6315a65d3744fd4d25b6c373 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 208028b3b6225b3c9eae3942e50ed243d8798b4b3a56b98a59b3f7e37baa55fd || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" efa6d8ef9f7ae0fd9f7d280fbff574d71882b60a357ae639e516dc173cf26986 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 8439d3394bc380fd17a920ee96df1d2272bf8d3490871d948ef750f95e0ded06 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" a8f90c768b54e28d61e02c1229b74a2b834e9852af523e5c70bcd2ae4c34a772 || goto error

View File

@@ -42,9 +42,9 @@ cd "%BUILDDIR%"
set FREETYPE=2.13.3
set HARFBUZZ=11.2.0
set LIBJPEGTURBO=3.1.0
set LIBPNG=1648
set SDL=SDL3-3.2.16
set LIBJPEGTURBO=3.1.1
set LIBPNG=1650
set SDL=SDL3-3.2.18
set QT=6.9.1
set QTMINOR=6.9
set LZ4=1.10.0
@@ -63,10 +63,10 @@ set SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 850cb5e38e21106c0abba86c5b73f8f74b9a32d7725505901d081080b0d3f0b3 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1648.zip 2e5f080360f77376eb2bfa9e2ed773b9c7728159aba47b638ad53ca839379040 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1650.zip 4be6938313b08d5921f9dede13f2789b653c96f4f8595d92ff3f09c9320e51c7 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" aadc97ea91f6ef078b0ae3a62bba69e008d9a7db19b34e4ac973b19b71b4217c || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 0cc7430fb827c1f843e31b8b26ba7f083b1eeb8f6315a65d3744fd4d25b6c373 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 208028b3b6225b3c9eae3942e50ed243d8798b4b3a56b98a59b3f7e37baa55fd || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" efa6d8ef9f7ae0fd9f7d280fbff574d71882b60a357ae639e516dc173cf26986 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 8439d3394bc380fd17a920ee96df1d2272bf8d3490871d948ef750f95e0ded06 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" a8f90c768b54e28d61e02c1229b74a2b834e9852af523e5c70bcd2ae4c34a772 || goto error

View File

@@ -9,7 +9,7 @@ cpuinfo is a library to detect essential for performance optimization informatio
## Features
- **Cross-platform** availability:
- Linux, Windows, macOS, Android, and iOS operating systems
- Linux, Windows, macOS, Android, iOS and FreeBSD operating systems
- x86, x86-64, ARM, and ARM64 architectures
- Modern **C/C++ interface**
- Thread-safe
@@ -258,6 +258,8 @@ LDFLAGS+= $(pkg-config --libs libcpuinfo)
- [x] x86
- [x] x86-64
- [x] arm64
- [x] FreeBSD
- [x] x86-64
## Methods

View File

@@ -353,6 +353,8 @@ enum cpuinfo_uarch {
cpuinfo_uarch_palm_cove = 0x0010020B,
/** Intel Sunny Cove microarchitecture (10 nm, Ice Lake). */
cpuinfo_uarch_sunny_cove = 0x0010020C,
/** Intel Willow Cove microarchitecture (10 nm, Tiger Lake). */
cpuinfo_uarch_willow_cove = 0x0010020D,
/** Pentium 4 with Willamette, Northwood, or Foster cores. */
cpuinfo_uarch_willamette = 0x00100300,
@@ -371,6 +373,10 @@ enum cpuinfo_uarch {
cpuinfo_uarch_goldmont = 0x00100404,
/** Intel Goldmont Plus microarchitecture (Gemini Lake). */
cpuinfo_uarch_goldmont_plus = 0x00100405,
/** Intel Gracemont microarchitecture (Twin Lake). */
cpuinfo_uarch_gracemont = 0x00100406,
/** Intel Crestmont microarchitecture (Sierra Forest). */
cpuinfo_uarch_crestmont = 0x00100407,
/** Intel Knights Ferry HPC boards. */
cpuinfo_uarch_knights_ferry = 0x00100500,
@@ -382,6 +388,8 @@ enum cpuinfo_uarch {
cpuinfo_uarch_knights_hill = 0x00100503,
/** Intel Knights Mill Xeon Phi. */
cpuinfo_uarch_knights_mill = 0x00100504,
/** Intel Darkmont microarchitecture (e-core used in Clearwater Forest). */
cpuinfo_uarch_darkmont = 0x00100505,
/** Intel/Marvell XScale series. */
cpuinfo_uarch_xscale = 0x00100600,
@@ -581,6 +589,22 @@ enum cpuinfo_uarch {
cpuinfo_uarch_avalanche = 0x0070010D,
/** Apple A15 / M2 processor (little cores). */
cpuinfo_uarch_blizzard = 0x0070010E,
/** Apple A16 processor (big cores). */
cpuinfo_uarch_everest = 0x00700200,
/** Apple A16 processor (little cores). */
cpuinfo_uarch_sawtooth = 0x00700201,
/** Apple A17 processor (big cores). */
cpuinfo_uarch_coll_everest = 0x00700202,
/** Apple A17 processor (little cores). */
cpuinfo_uarch_coll_sawtooth = 0x00700203,
/** Apple A18 processor (big cores). */
cpuinfo_uarch_tupai_everest = 0x00700204,
/** Apple A18 processor (little cores). */
cpuinfo_uarch_tupai_sawtooth = 0x00700205,
/** Apple A18 pro processor (big cores). */
cpuinfo_uarch_tahiti_everest = 0x00700206,
/** Apple A18 pro processor (little cores). */
cpuinfo_uarch_tahiti_sawtooth = 0x00700207,
/** Cavium ThunderX. */
cpuinfo_uarch_thunderx = 0x00800100,
@@ -1700,6 +1724,7 @@ struct cpuinfo_arm_isa {
bool sme_b16b16;
bool sme_f16f16;
uint32_t svelen;
uint32_t smelen;
#endif
bool rdm;
bool fp16arith;
@@ -2081,6 +2106,15 @@ static inline uint32_t cpuinfo_get_max_arm_sve_length(void) {
#endif
}
// Function to get the max SME vector length on ARM CPU's which support SME.
static inline uint32_t cpuinfo_get_max_arm_sme_length(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.smelen * 8; // bytes * 8 = bit length(vector length)
#else
return 0;
#endif
}
static inline bool cpuinfo_has_arm_sme(void) {
#if CPUINFO_ARCH_ARM64
return cpuinfo_isa.sme;

View File

@@ -149,6 +149,8 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc T310");
} else if (chipset->series == cpuinfo_arm_chipset_series_unisoc_ums && chipset->model == 312) {
cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc UMS312");
} else if (chipset->vendor == cpuinfo_arm_chipset_vendor_unknown) {
cpuinfo_log_warning("VDOT instructions disabled: unknown chipset");
} else {
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case UINT32_C(0x4100D0B0): /* Cortex-A76 */

View File

@@ -191,4 +191,21 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
// Mask out the SVE vector length bits
isa->svelen = ret & PR_SVE_VL_LEN_MASK;
}
#ifndef PR_SME_GET_VL
#define PR_SME_GET_VL 64
#endif
#ifndef PR_SME_VL_LEN_MASK
#define PR_SME_VL_LEN_MASK 0xffff
#endif
ret = prctl(PR_SME_GET_VL);
if (ret < 0) {
cpuinfo_log_warning("No SME support on this machine");
isa->smelen = 0; // Assume no SME support if the call fails
} else {
// Mask out the SME vector length bits
isa->smelen = ret & PR_SME_VL_LEN_MASK;
}
}

View File

@@ -27,6 +27,45 @@
#ifndef CPUFAMILY_ARM_AVALANCHE_BLIZZARD
#define CPUFAMILY_ARM_AVALANCHE_BLIZZARD 0xDA33D83D
#endif
// Following are copied over from ncnn/src/cpu.cpp
// A16
#ifndef CPUFAMILY_ARM_EVEREST_SAWTOOTH
#define CPUFAMILY_ARM_EVEREST_SAWTOOTH 0x8765edea
#endif
// A17
#ifndef CPUFAMILY_ARM_COLL
#define CPUFAMILY_ARM_COLL 0x2876f5b5
#endif
// A18
#ifndef CPUFAMILY_ARM_TUPAI
#define CPUFAMILY_ARM_TUPAI 0x204526d0
#endif
// A18 Pro
#ifndef CPUFAMILY_ARM_TAHITI
#define CPUFAMILY_ARM_TAHITI 0x75d4acb9
#endif
// For M3/M4 we need to populate more information about
// efficiency and perf cores.
// M3
#ifndef CPUFAMILY_ARM_IBIZA
#define CPUFAMILY_ARM_IBIZA 0xfa33415e
#endif
// M3 Pro
#ifndef CPUFAMILY_ARM_LOBOS
#define CPUFAMILY_ARM_LOBOS 0x5f4dea93
#endif
// M3 Max
#ifndef CPUFAMILY_ARM_PALMA
#define CPUFAMILY_ARM_PALMA 0x72015832
#endif
// M4
#ifndef CPUFAMILY_ARM_DONAN
#define CPUFAMILY_ARM_DONAN 0x6f5129ac
#endif
// M4 Pro / M4 Max
#ifndef CPUFAMILY_ARM_BRAVA
#define CPUFAMILY_ARM_BRAVA 0x17d5b93a
#endif
struct cpuinfo_arm_isa cpuinfo_isa = {
.aes = true,
@@ -93,6 +132,23 @@ static enum cpuinfo_uarch decode_uarch(uint32_t cpu_family, uint32_t core_index,
case CPUFAMILY_ARM_AVALANCHE_BLIZZARD:
/* Hexa-core: 2x Avalanche + 4x Blizzard */
return core_index + 4 < core_count ? cpuinfo_uarch_avalanche : cpuinfo_uarch_blizzard;
case CPUFAMILY_ARM_EVEREST_SAWTOOTH:
/* Hexa-core: 2x Avalanche + 4x Blizzard */
return core_index + 4 < core_count ? cpuinfo_uarch_everest : cpuinfo_uarch_sawtooth;
return core_index + 4 < core_count ? cpuinfo_uarch_avalanche : cpuinfo_uarch_blizzard;
case CPUFAMILY_ARM_COLL:
/* Hexa-core: 2x Avalanche + 4x Blizzard */
return core_index + 4 < core_count ? cpuinfo_uarch_coll_everest : cpuinfo_uarch_coll_sawtooth;
case CPUFAMILY_ARM_TUPAI:
/* Hexa-core: 2x Avalanche + 4x Blizzard */
return core_index + 4 < core_count ? cpuinfo_uarch_tupai_everest : cpuinfo_uarch_tupai_sawtooth;
case CPUFAMILY_ARM_TAHITI:
/* Hexa-core: 2x Avalanche + 4x Blizzard */
return core_index + 4 < core_count ? cpuinfo_uarch_tahiti_everest
: cpuinfo_uarch_tahiti_sawtooth;
default:
/* Use hw.cpusubtype for detection */
break;

View File

@@ -112,7 +112,7 @@ void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
*
* for more details.
*/
int ret = syscall(NR_riscv_hwprobe, pairs, pairs_count, cpu_set_size, (unsigned long*)cpu_set, 0 /* flags */);
int ret = syscall(NR_riscv_hwprobe, pairs, pairs_count, cpu_set_size, cpu_set, 0 /* flags */);
#else
int ret = __riscv_hwprobe(pairs, pairs_count, cpu_set_size, (unsigned long*)cpu_set, 0 /* flags */);
#endif

View File

@@ -80,11 +80,7 @@ struct proc_cpuinfo_parser_state {
* Decode a single line of /proc/cpuinfo information.
* Lines have format <words-with-spaces>[ ]*:[ ]<space-separated words>
*/
static bool parse_line(
const char* line_start,
const char* line_end,
void* context,
uint64_t line_number) {
static bool parse_line(const char* line_start, const char* line_end, void* context, uint64_t line_number) {
struct proc_cpuinfo_parser_state* restrict state = context;
/* Empty line. Skip. */
if (line_start == line_end) {
@@ -215,6 +211,5 @@ bool cpuinfo_x86_linux_parse_proc_cpuinfo(
.max_processors_count = max_processors_count,
.processors = processors,
};
return cpuinfo_linux_parse_multiline_file(
"/proc/cpuinfo", BUFFER_SIZE, parse_line, &state);
return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE, parse_line, &state);
}

View File

@@ -168,6 +168,9 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
case 0x7E: // Ice Lake-U
return cpuinfo_uarch_sunny_cove;
case 0x8C: // Tiger U
case 0x8D: // Tiger H
return cpuinfo_uarch_willow_cove;
/* Low-power cores */
case 0x1C: // Diamondville,
// Silverthorne,
@@ -185,6 +188,10 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
case 0x5A: // Moorefield
case 0x5D: // SoFIA
return cpuinfo_uarch_silvermont;
case 0xBE: // Twin Lake
return cpuinfo_uarch_gracemont;
case 0xAF: // Sierra Forest
return cpuinfo_uarch_crestmont;
case 0x4C: // Braswell, Cherry
// Trail
case 0x75: // Spreadtrum
@@ -201,6 +208,8 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
return cpuinfo_uarch_knights_landing;
case 0x85:
return cpuinfo_uarch_knights_mill;
case 0xDD: // Clearwater Forest
return cpuinfo_uarch_darkmont;
}
break;
case 0x0F:

View File

@@ -47,11 +47,11 @@ PR is acceptable as an alternative.
## The Google Test and Google Mock Communities
The Google Test community exists primarily through the
[discussion group](http://groups.google.com/group/googletestframework) and the
[discussion group](https://groups.google.com/group/googletestframework) and the
GitHub repository. Likewise, the Google Mock community exists primarily through
their own [discussion group](http://groups.google.com/group/googlemock). You are
definitely encouraged to contribute to the discussion and you can also help us
to keep the effectiveness of the group high by following and promoting the
their own [discussion group](https://groups.google.com/group/googlemock). You
are definitely encouraged to contribute to the discussion and you can also help
us to keep the effectiveness of the group high by following and promoting the
guidelines listed here.
### Please Be Friendly

View File

@@ -2,27 +2,18 @@
### Announcements
#### Live at Head
GoogleTest now follows the
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support).
We recommend
[updating to the latest commit in the `main` branch as often as possible](https://github.com/abseil/abseil-cpp/blob/master/FAQ.md#what-is-live-at-head-and-how-do-i-do-it).
We do publish occasional semantic versions, tagged with
`v${major}.${minor}.${patch}` (e.g. `v1.16.0`).
#### Documentation Updates
Our documentation is now live on GitHub Pages at
https://google.github.io/googletest/. We recommend browsing the documentation on
GitHub Pages rather than directly in the repository.
#### Release 1.16.0
#### Release 1.17.0
[Release 1.16.0](https://github.com/google/googletest/releases/tag/v1.16.0) is
[Release 1.17.0](https://github.com/google/googletest/releases/tag/v1.17.0) is
now available.
The 1.16.x branch requires at least C++14.
The 1.17.x branch [requires at least C++17]((https://opensource.google/documentation/policies/cplusplus-support#c_language_standard).
#### Continuous Integration

View File

@@ -835,6 +835,10 @@ class Action<R(Args...)> {
Result operator()(const InArgs&...) const {
return function_impl();
}
template <typename... InArgs>
Result operator()(const InArgs&...) {
return function_impl();
}
FunctionImpl function_impl;
};
@@ -1451,6 +1455,30 @@ struct WithArgsAction {
return OA{std::move(inner_action)};
}
// As above, but in the case where we want to create a OnceAction from a const
// WithArgsAction. This is fine as long as the inner action doesn't need to
// move any of its state to create a OnceAction.
template <
typename R, typename... Args,
typename std::enable_if<
std::is_convertible<const InnerAction&,
OnceAction<R(internal::TupleElement<
I, std::tuple<Args...>>...)>>::value,
int>::type = 0>
operator OnceAction<R(Args...)>() const& { // NOLINT
struct OA {
OnceAction<InnerSignature<R, Args...>> inner_action;
R operator()(Args&&... args) && {
return std::move(inner_action)
.Call(std::get<I>(
std::forward_as_tuple(std::forward<Args>(args)...))...);
}
};
return OA{inner_action};
}
template <
typename R, typename... Args,
typename std::enable_if<
@@ -1703,9 +1731,8 @@ template <size_t k>
struct ReturnArgAction {
template <typename... Args,
typename = typename std::enable_if<(k < sizeof...(Args))>::type>
auto operator()(Args&&... args) const
-> decltype(std::get<k>(
std::forward_as_tuple(std::forward<Args>(args)...))) {
auto operator()(Args&&... args) const -> decltype(std::get<k>(
std::forward_as_tuple(std::forward<Args>(args)...))) {
return std::get<k>(std::forward_as_tuple(std::forward<Args>(args)...));
}
};
@@ -1720,6 +1747,16 @@ struct SaveArgAction {
}
};
template <size_t k, typename Ptr>
struct SaveArgByMoveAction {
Ptr pointer;
template <typename... Args>
void operator()(Args&&... args) const {
*pointer = std::move(std::get<k>(std::tie(args...)));
}
};
template <size_t k, typename Ptr>
struct SaveArgPointeeAction {
Ptr pointer;
@@ -2070,6 +2107,13 @@ internal::SaveArgAction<k, Ptr> SaveArg(Ptr pointer) {
return {pointer};
}
// Action SaveArgByMove<k>(pointer) moves the k-th (0-based) argument of the
// mock function into *pointer.
template <size_t k, typename Ptr>
internal::SaveArgByMoveAction<k, Ptr> SaveArgByMove(Ptr pointer) {
return {pointer};
}
// Action SaveArgPointee<k>(pointer) saves the value pointed to
// by the k-th (0-based) argument of the mock function to *pointer.
template <size_t k, typename Ptr>
@@ -2213,9 +2257,9 @@ template <typename F, typename Impl>
}
#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \
, GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED const arg##i##_type& arg##i
#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED const args_type& args GMOCK_PP_REPEAT( \
, [[maybe_unused]] const arg##i##_type& arg##i
#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \
[[maybe_unused]] const args_type& args GMOCK_PP_REPEAT( \
GMOCK_INTERNAL_ARG_UNUSED, , 10)
#define GMOCK_INTERNAL_ARG(i, data, el) , const arg##i##_type& arg##i
@@ -2280,8 +2324,8 @@ template <typename F, typename Impl>
std::shared_ptr<const gmock_Impl> impl_; \
}; \
template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \
inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name( \
GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) GTEST_MUST_USE_RESULT_; \
[[nodiscard]] inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name( \
GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)); \
template <GMOCK_ACTION_TYPENAME_PARAMS_(params)> \
inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name( \
GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) { \
@@ -2316,7 +2360,7 @@ template <typename F, typename Impl>
return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
}; \
}; \
inline name##Action name() GTEST_MUST_USE_RESULT_; \
[[nodiscard]] inline name##Action name(); \
inline name##Action name() { return name##Action(); } \
template <typename function_type, typename return_type, typename args_type, \
GMOCK_ACTION_TEMPLATE_ARGS_NAMES_> \

View File

@@ -257,6 +257,7 @@
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <exception>
#include <functional>
#include <initializer_list>
@@ -1311,6 +1312,15 @@ class AllOfMatcherImpl : public MatcherInterface<const T&> {
bool MatchAndExplain(const T& x,
MatchResultListener* listener) const override {
if (!listener->IsInterested()) {
// Fast path to avoid unnecessary formatting.
for (const Matcher<T>& matcher : matchers_) {
if (!matcher.Matches(x)) {
return false;
}
}
return true;
}
// This method uses matcher's explanation when explaining the result.
// However, if matcher doesn't provide one, this method uses matcher's
// description.
@@ -1430,6 +1440,15 @@ class AnyOfMatcherImpl : public MatcherInterface<const T&> {
bool MatchAndExplain(const T& x,
MatchResultListener* listener) const override {
if (!listener->IsInterested()) {
// Fast path to avoid unnecessary formatting of match explanations.
for (const Matcher<T>& matcher : matchers_) {
if (matcher.Matches(x)) {
return true;
}
}
return false;
}
// This method uses matcher's explanation when explaining the result.
// However, if matcher doesn't provide one, this method uses matcher's
// description.
@@ -2097,11 +2116,11 @@ class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> {
template <typename Class, typename FieldType>
class FieldMatcher {
public:
FieldMatcher(FieldType Class::*field,
FieldMatcher(FieldType Class::* field,
const Matcher<const FieldType&>& matcher)
: field_(field), matcher_(matcher), whose_field_("whose given field ") {}
FieldMatcher(const std::string& field_name, FieldType Class::*field,
FieldMatcher(const std::string& field_name, FieldType Class::* field,
const Matcher<const FieldType&>& matcher)
: field_(field),
matcher_(matcher),
@@ -2145,7 +2164,7 @@ class FieldMatcher {
return MatchAndExplainImpl(std::false_type(), *p, listener);
}
const FieldType Class::*field_;
const FieldType Class::* field_;
const Matcher<const FieldType&> matcher_;
// Contains either "whose given field " if the name of the field is unknown
@@ -2855,6 +2874,54 @@ class ContainsMatcherImpl : public QuantifierMatcherImpl<Container> {
}
};
// Implements DistanceFrom(target, get_distance, distance_matcher) for the given
// argument types:
// * V is the type of the value to be matched.
// * T is the type of the target value.
// * Distance is the type of the distance between V and T.
// * GetDistance is the type of the functor for computing the distance between
// V and T.
template <typename V, typename T, typename Distance, typename GetDistance>
class DistanceFromMatcherImpl : public MatcherInterface<V> {
public:
// Arguments:
// * target: the target value.
// * get_distance: the functor for computing the distance between the value
// being matched and target.
// * distance_matcher: the matcher for checking the distance.
DistanceFromMatcherImpl(T target, GetDistance get_distance,
Matcher<const Distance&> distance_matcher)
: target_(std::move(target)),
get_distance_(std::move(get_distance)),
distance_matcher_(std::move(distance_matcher)) {}
// Describes what this matcher does.
void DescribeTo(::std::ostream* os) const override {
distance_matcher_.DescribeTo(os);
*os << " away from " << PrintToString(target_);
}
void DescribeNegationTo(::std::ostream* os) const override {
distance_matcher_.DescribeNegationTo(os);
*os << " away from " << PrintToString(target_);
}
bool MatchAndExplain(V value, MatchResultListener* listener) const override {
const auto distance = get_distance_(value, target_);
const bool match = distance_matcher_.Matches(distance);
if (!match && listener->IsInterested()) {
*listener << "which is " << PrintToString(distance) << " away from "
<< PrintToString(target_);
}
return match;
}
private:
const T target_;
const GetDistance get_distance_;
const Matcher<const Distance&> distance_matcher_;
};
// Implements Each(element_matcher) for the given argument type Container.
// Symmetric to ContainsMatcherImpl.
template <typename Container>
@@ -2990,6 +3057,52 @@ auto Second(T& x, Rank1) -> decltype((x.second)) { // NOLINT
}
} // namespace pair_getters
// Default functor for computing the distance between two values.
struct DefaultGetDistance {
template <typename T, typename U>
auto operator()(const T& lhs, const U& rhs) const {
using std::abs;
// Allow finding abs() in the type's namespace via ADL.
return abs(lhs - rhs);
}
};
// Implements polymorphic DistanceFrom(target, get_distance, distance_matcher)
// matcher. Template arguments:
// * T is the type of the target value.
// * GetDistance is the type of the functor for computing the distance between
// the value being matched and the target.
// * DistanceMatcher is the type of the matcher for checking the distance.
template <typename T, typename GetDistance, typename DistanceMatcher>
class DistanceFromMatcher {
public:
// Arguments:
// * target: the target value.
// * get_distance: the functor for computing the distance between the value
// being matched and target.
// * distance_matcher: the matcher for checking the distance.
DistanceFromMatcher(T target, GetDistance get_distance,
DistanceMatcher distance_matcher)
: target_(std::move(target)),
get_distance_(std::move(get_distance)),
distance_matcher_(std::move(distance_matcher)) {}
DistanceFromMatcher(const DistanceFromMatcher& other) = default;
// Implicitly converts to a monomorphic matcher of the given type.
template <typename V>
operator Matcher<V>() const { // NOLINT
using Distance = decltype(get_distance_(std::declval<V>(), target_));
return Matcher<V>(new DistanceFromMatcherImpl<V, T, Distance, GetDistance>(
target_, get_distance_, distance_matcher_));
}
private:
const T target_;
const GetDistance get_distance_;
const DistanceMatcher distance_matcher_;
};
// Implements Key(inner_matcher) for the given argument pair type.
// Key(inner_matcher) matches an std::pair whose 'first' field matches
// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an
@@ -3197,8 +3310,8 @@ class PairMatcher {
};
template <typename T, size_t... I>
auto UnpackStructImpl(const T& t, std::index_sequence<I...>,
int) -> decltype(std::tie(get<I>(t)...)) {
auto UnpackStructImpl(const T& t, std::index_sequence<I...>, int)
-> decltype(std::tie(get<I>(t)...)) {
static_assert(std::tuple_size<T>::value == sizeof...(I),
"Number of arguments doesn't match the number of fields.");
return std::tie(get<I>(t)...);
@@ -3305,6 +3418,21 @@ auto UnpackStructImpl(const T& u, std::make_index_sequence<20>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t] = u;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t);
}
template <typename T>
auto UnpackStructImpl(const T& in, std::make_index_sequence<21>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u] =
in;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t,
u);
}
template <typename T>
auto UnpackStructImpl(const T& in, std::make_index_sequence<22>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
v] = in;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
v);
}
#endif // defined(__cpp_structured_bindings)
template <size_t I, typename T>
@@ -3480,7 +3608,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
StlContainerReference stl_container = View::ConstReference(container);
auto it = stl_container.begin();
size_t exam_pos = 0;
bool mismatch_found = false; // Have we found a mismatched element yet?
bool unmatched_found = false;
// Go through the elements and matchers in pairs, until we reach
// the end of either the elements or the matchers, or until we find a
@@ -3496,11 +3624,23 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
}
if (!match) {
mismatch_found = true;
unmatched_found = true;
// We cannot store the iterator for the unmatched element to be used
// later, as some users use ElementsAre() with a "container" whose
// iterator is not copy-constructible or copy-assignable.
//
// We cannot store a pointer to the element either, as some container's
// iterators return a temporary.
//
// We cannot store the element itself either, as the element may not be
// copyable.
//
// Therefore, we just remember the index of the unmatched element,
// and use it later to print the unmatched element.
break;
}
}
// If mismatch_found is true, 'exam_pos' is the index of the mismatch.
// If unmatched_found is true, exam_pos is the index of the mismatch.
// Find how many elements the actual container has. We avoid
// calling size() s.t. this code works for stream-like "containers"
@@ -3521,10 +3661,27 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
return false;
}
if (mismatch_found) {
if (unmatched_found) {
// The element count matches, but the exam_pos-th element doesn't match.
if (listener_interested) {
*listener << "whose element #" << exam_pos << " doesn't match";
// Find the unmatched element.
auto unmatched_it = stl_container.begin();
// We cannot call std::advance() on the iterator, as some users use
// ElementsAre() with a "container" whose iterator is incompatible with
// std::advance() (e.g. it may not have the difference_type member
// type).
for (size_t i = 0; i != exam_pos; ++i) {
++unmatched_it;
}
// If the array is long or the elements' print-out is large, it may be
// hard for the user to find the mismatched element and its
// corresponding matcher description. Therefore we print the index, the
// value of the mismatched element, and the corresponding matcher
// description to ease debugging.
*listener << "whose element #" << exam_pos << " ("
<< PrintToString(*unmatched_it) << ") ";
matchers_[exam_pos].DescribeNegationTo(listener->stream());
PrintIfNotEmpty(explanations[exam_pos], listener->stream());
}
return false;
@@ -3930,15 +4087,15 @@ GTEST_API_ std::string FormatMatcherDescription(
// Overloads to support `OptionalMatcher` being used with a type that either
// supports implicit conversion to bool or a `has_value()` method.
template <typename Optional>
auto IsOptionalEngaged(const Optional& optional,
Rank1) -> decltype(!!optional) {
auto IsOptionalEngaged(const Optional& optional, Rank1)
-> decltype(!!optional) {
// The use of double-negation here is to preserve historical behavior where
// the matcher used `operator!` rather than directly using `operator bool`.
return !static_cast<bool>(!optional);
}
template <typename Optional>
auto IsOptionalEngaged(const Optional& optional,
Rank0) -> decltype(!optional.has_value()) {
auto IsOptionalEngaged(const Optional& optional, Rank0)
-> decltype(!optional.has_value()) {
return optional.has_value();
}
@@ -3979,6 +4136,10 @@ class OptionalMatcher {
return false;
}
const ValueType& value = *optional;
if (!listener->IsInterested()) {
// Fast path to avoid unnecessary generation of match explanation.
return value_matcher_.Matches(value);
}
StringMatchResultListener value_listener;
const bool match = value_matcher_.MatchAndExplain(value, &value_listener);
*listener << "whose value " << PrintToString(value)
@@ -4365,6 +4526,42 @@ inline internal::FloatingEqMatcher<double> DoubleNear(double rhs,
return internal::FloatingEqMatcher<double>(rhs, false, max_abs_error);
}
// The DistanceFrom(target, get_distance, m) and DistanceFrom(target, m)
// matchers work on arbitrary types that have the "distance" concept. What they
// do:
//
// 1. compute the distance between the value and the target using
// get_distance(value, target) if get_distance is provided; otherwise compute
// the distance as abs(value - target).
// 2. match the distance against the user-provided matcher m; if the match
// succeeds, the DistanceFrom() match succeeds.
//
// Examples:
//
// // 0.5's distance from 0.6 should be <= 0.2.
// EXPECT_THAT(0.5, DistanceFrom(0.6, Le(0.2)));
//
// Vector2D v1(3.0, 4.0), v2(3.2, 6.0);
// // v1's distance from v2, as computed by EuclideanDistance(v1, v2),
// // should be >= 1.0.
// EXPECT_THAT(v1, DistanceFrom(v2, EuclideanDistance, Ge(1.0)));
template <typename T, typename GetDistance, typename DistanceMatcher>
inline internal::DistanceFromMatcher<T, GetDistance, DistanceMatcher>
DistanceFrom(T target, GetDistance get_distance,
DistanceMatcher distance_matcher) {
return internal::DistanceFromMatcher<T, GetDistance, DistanceMatcher>(
std::move(target), std::move(get_distance), std::move(distance_matcher));
}
template <typename T, typename DistanceMatcher>
inline internal::DistanceFromMatcher<T, internal::DefaultGetDistance,
DistanceMatcher>
DistanceFrom(T target, DistanceMatcher distance_matcher) {
return DistanceFrom(std::move(target), internal::DefaultGetDistance(),
std::move(distance_matcher));
}
// Creates a matcher that matches any double argument approximately equal to
// rhs, up to the specified max absolute error bound, including NaN values when
// rhs is NaN. The max absolute error bound must be non-negative.
@@ -4430,7 +4627,7 @@ WhenDynamicCastTo(const Matcher<To>& inner_matcher) {
// matches a Foo object x if and only if x.number >= 5.
template <typename Class, typename FieldType, typename FieldMatcher>
inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
FieldType Class::*field, const FieldMatcher& matcher) {
FieldType Class::* field, const FieldMatcher& matcher) {
return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
field, MatcherCast<const FieldType&>(matcher)));
// The call to MatcherCast() is required for supporting inner
@@ -4443,7 +4640,7 @@ inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
// messages.
template <typename Class, typename FieldType, typename FieldMatcher>
inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
const std::string& field_name, FieldType Class::*field,
const std::string& field_name, FieldType Class::* field,
const FieldMatcher& matcher) {
return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
field_name, field, MatcherCast<const FieldType&>(matcher)));
@@ -4453,6 +4650,10 @@ inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
// matches 'matcher'. For example,
// Property(&Foo::str, StartsWith("hi"))
// matches a Foo object x if and only if x.str() starts with "hi".
//
// Warning: Don't use `Property()` against member functions that you do not
// own, because taking addresses of functions is fragile and generally not part
// of the contract of the function.
template <typename Class, typename PropertyType, typename PropertyMatcher>
inline PolymorphicMatcher<internal::PropertyMatcher<
Class, PropertyType, PropertyType (Class::*)() const>>
@@ -5551,8 +5752,7 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
template <typename arg_type> \
bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain( \
const arg_type& arg, \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED ::testing::MatchResultListener* \
result_listener) const
[[maybe_unused]] ::testing::MatchResultListener* result_listener) const
#define MATCHER_P(name, p0, description) \
GMOCK_INTERNAL_MATCHER(name, name##MatcherP, description, (#p0), (p0))
@@ -5637,8 +5837,8 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
bool full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>:: \
gmock_Impl<arg_type>::MatchAndExplain( \
const arg_type& arg, \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED ::testing:: \
MatchResultListener* result_listener) const
[[maybe_unused]] ::testing::MatchResultListener* result_listener) \
const
#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args) \
GMOCK_PP_TAIL( \

View File

@@ -521,9 +521,8 @@
GMOCK_INTERNAL_DECL_##value_params) \
GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params), \
= default; \
, \
: impl_(std::make_shared<gmock_Impl>( \
GMOCK_INTERNAL_LIST_##value_params)){}) \
, : impl_(std::make_shared<gmock_Impl>( \
GMOCK_INTERNAL_LIST_##value_params)){}) \
GMOCK_ACTION_CLASS_(name, value_params)(const GMOCK_ACTION_CLASS_( \
name, value_params) &) noexcept GMOCK_INTERNAL_DEFN_COPY_ \
##value_params \
@@ -551,10 +550,10 @@
}; \
template <GMOCK_INTERNAL_DECL_##template_params \
GMOCK_INTERNAL_DECL_TYPE_##value_params> \
GMOCK_ACTION_CLASS_( \
[[nodiscard]] GMOCK_ACTION_CLASS_( \
name, value_params)<GMOCK_INTERNAL_LIST_##template_params \
GMOCK_INTERNAL_LIST_TYPE_##value_params> \
name(GMOCK_INTERNAL_DECL_##value_params) GTEST_MUST_USE_RESULT_; \
name(GMOCK_INTERNAL_DECL_##value_params); \
template <GMOCK_INTERNAL_DECL_##template_params \
GMOCK_INTERNAL_DECL_TYPE_##value_params> \
inline GMOCK_ACTION_CLASS_( \

View File

@@ -467,11 +467,6 @@ struct Function<R(Args...)> {
using MakeResultIgnoredValue = IgnoredValue(Args...);
};
#ifdef GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
template <typename R, typename... Args>
constexpr size_t Function<R(Args...)>::ArgumentCount;
#endif
// Workaround for MSVC error C2039: 'type': is not a member of 'std'
// when std::tuple_element is used.
// See: https://github.com/google/googletest/issues/3931

View File

@@ -25,7 +25,7 @@ When building GoogleTest as a standalone project, the typical workflow starts
with
```
git clone https://github.com/google/googletest.git -b v1.16.0
git clone https://github.com/google/googletest.git -b v1.17.0
cd googletest # Main directory of the cloned repository.
mkdir build # Create a directory to hold the build output.
cd build
@@ -124,9 +124,9 @@ match the project in which it is included.
#### C++ Standard Version
An environment that supports C++14 is required in order to successfully build
An environment that supports C++17 is required in order to successfully build
GoogleTest. One way to ensure this is to specify the standard in the top-level
project, for example by using the `set(CMAKE_CXX_STANDARD 14)` command along
project, for example by using the `set(CMAKE_CXX_STANDARD 17)` command along
with `set(CMAKE_CXX_STANDARD_REQUIRED ON)`. If this is not feasible, for example
in a C project using GoogleTest for validation, then it can be specified by
adding it to the options for cmake via the`-DCMAKE_CXX_FLAGS` option.

View File

@@ -129,6 +129,13 @@ namespace testing {
//
// Expected: Foo() is even
// Actual: it's 5
//
// Returned AssertionResult objects may not be ignored.
// Note: Disabled for SWIG as it doesn't parse attributes correctly.
#if !defined(SWIG)
class [[nodiscard]] AssertionResult;
#endif // !SWIG
class GTEST_API_ AssertionResult {
public:

View File

@@ -174,6 +174,7 @@ TEST_P(DerivedTest, DoesBlah) {
#endif // 0
#include <functional>
#include <iterator>
#include <utility>
@@ -413,7 +414,8 @@ internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) {
// Synopsis:
// ConvertGenerator<T>(gen)
// - returns a generator producing the same elements as generated by gen, but
// each element is static_cast to type T before being returned
// each T-typed element is static_cast to a type deduced from the interface
// that accepts this generator, and then returned
//
// It is useful when using the Combine() function to get the generated
// parameters in a custom type instead of std::tuple
@@ -441,10 +443,65 @@ internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) {
// Combine(Values("cat", "dog"),
// Values(BLACK, WHITE))));
//
template <typename T>
internal::ParamConverterGenerator<T> ConvertGenerator(
internal::ParamGenerator<T> gen) {
return internal::ParamConverterGenerator<T>(gen);
template <typename RequestedT>
internal::ParamConverterGenerator<RequestedT> ConvertGenerator(
internal::ParamGenerator<RequestedT> gen) {
return internal::ParamConverterGenerator<RequestedT>(std::move(gen));
}
// As above, but takes a callable as a second argument. The callable converts
// the generated parameter to the test fixture's parameter type. This allows you
// to use a parameter type that does not have a converting constructor from the
// generated type.
//
// Example:
//
// This will instantiate tests in test suite AnimalTest each one with
// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
// tuple("dog", BLACK), and tuple("dog", WHITE):
//
// enum Color { BLACK, GRAY, WHITE };
// struct ParamType {
// std::string animal;
// Color color;
// };
// class AnimalTest
// : public testing::TestWithParam<ParamType> {...};
//
// TEST_P(AnimalTest, AnimalLooksNice) {...}
//
// INSTANTIATE_TEST_SUITE_P(
// AnimalVariations, AnimalTest,
// ConvertGenerator(Combine(Values("cat", "dog"), Values(BLACK, WHITE)),
// [](std::tuple<std::string, Color> t) {
// return ParamType{.animal = std::get<0>(t),
// .color = std::get<1>(t)};
// }));
//
template <typename T, int&... ExplicitArgumentBarrier, typename Gen,
typename Func,
typename StdFunction = decltype(std::function(std::declval<Func>()))>
internal::ParamConverterGenerator<T, StdFunction> ConvertGenerator(Gen&& gen,
Func&& f) {
return internal::ParamConverterGenerator<T, StdFunction>(
std::forward<Gen>(gen), std::forward<Func>(f));
}
// As above, but infers the T from the supplied std::function instead of
// having the caller specify it.
template <int&... ExplicitArgumentBarrier, typename Gen, typename Func,
typename StdFunction = decltype(std::function(std::declval<Func>()))>
auto ConvertGenerator(Gen&& gen, Func&& f) {
constexpr bool is_single_arg_std_function =
internal::IsSingleArgStdFunction<StdFunction>::value;
if constexpr (is_single_arg_std_function) {
return ConvertGenerator<
typename internal::FuncSingleParamType<StdFunction>::type>(
std::forward<Gen>(gen), std::forward<Func>(f));
} else {
static_assert(is_single_arg_std_function,
"The call signature must contain a single argument.");
}
}
#define TEST_P(test_suite_name, test_name) \
@@ -469,7 +526,7 @@ internal::ParamConverterGenerator<T> ConvertGenerator(
::testing::internal::CodeLocation(__FILE__, __LINE__)); \
return 0; \
} \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static int gtest_registering_dummy_; \
[[maybe_unused]] static int gtest_registering_dummy_; \
}; \
int GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)::gtest_registering_dummy_ = \
@@ -493,39 +550,38 @@ internal::ParamConverterGenerator<T> ConvertGenerator(
#define GTEST_GET_FIRST_(first, ...) first
#define GTEST_GET_SECOND_(first, second, ...) second
#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \
static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \
gtest_##prefix##test_suite_name##_EvalGenerator_() { \
return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \
} \
static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \
const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \
if (::testing::internal::AlwaysFalse()) { \
::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \
__VA_ARGS__, \
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
DUMMY_PARAM_))); \
auto t = std::make_tuple(__VA_ARGS__); \
static_assert(std::tuple_size<decltype(t)>::value <= 2, \
"Too Many Args!"); \
} \
return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \
__VA_ARGS__, \
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
DUMMY_PARAM_))))(info); \
} \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static int \
gtest_##prefix##test_suite_name##_dummy_ = \
::testing::UnitTest::GetInstance() \
->parameterized_test_registry() \
.GetTestSuitePatternHolder<test_suite_name>( \
GTEST_STRINGIFY_(test_suite_name), \
::testing::internal::CodeLocation(__FILE__, __LINE__)) \
->AddTestSuiteInstantiation( \
GTEST_STRINGIFY_(prefix), \
&gtest_##prefix##test_suite_name##_EvalGenerator_, \
&gtest_##prefix##test_suite_name##_EvalGenerateName_, \
__FILE__, __LINE__)
#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \
static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \
gtest_##prefix##test_suite_name##_EvalGenerator_() { \
return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \
} \
static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \
const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \
if (::testing::internal::AlwaysFalse()) { \
::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \
__VA_ARGS__, \
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
DUMMY_PARAM_))); \
auto t = std::make_tuple(__VA_ARGS__); \
static_assert(std::tuple_size<decltype(t)>::value <= 2, \
"Too Many Args!"); \
} \
return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \
__VA_ARGS__, \
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
DUMMY_PARAM_))))(info); \
} \
[[maybe_unused]] static int gtest_##prefix##test_suite_name##_dummy_ = \
::testing::UnitTest::GetInstance() \
->parameterized_test_registry() \
.GetTestSuitePatternHolder<test_suite_name>( \
GTEST_STRINGIFY_(test_suite_name), \
::testing::internal::CodeLocation(__FILE__, __LINE__)) \
->AddTestSuiteInstantiation( \
GTEST_STRINGIFY_(prefix), \
&gtest_##prefix##test_suite_name##_EvalGenerator_, \
&gtest_##prefix##test_suite_name##_EvalGenerateName_, __FILE__, \
__LINE__)
// Allow Marking a Parameterized test class as not needing to be instantiated.
#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \

View File

@@ -194,34 +194,33 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
GTEST_NAME_GENERATOR_(CaseName)
#define TYPED_TEST(CaseName, TestName) \
static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \
"test-name must not be empty"); \
template <typename gtest_TypeParam_> \
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
: public CaseName<gtest_TypeParam_> { \
private: \
typedef CaseName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \
gtest_##CaseName##_##TestName##_registered_ = \
::testing::internal::TypeParameterizedTest< \
CaseName, \
::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_( \
CaseName, TestName)>, \
GTEST_TYPE_PARAMS_( \
CaseName)>::Register("", \
::testing::internal::CodeLocation( \
__FILE__, __LINE__), \
GTEST_STRINGIFY_(CaseName), \
GTEST_STRINGIFY_(TestName), 0, \
::testing::internal::GenerateNames< \
GTEST_NAME_GENERATOR_(CaseName), \
GTEST_TYPE_PARAMS_(CaseName)>()); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, \
#define TYPED_TEST(CaseName, TestName) \
static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \
"test-name must not be empty"); \
template <typename gtest_TypeParam_> \
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
: public CaseName<gtest_TypeParam_> { \
private: \
typedef CaseName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
[[maybe_unused]] static bool gtest_##CaseName##_##TestName##_registered_ = \
::testing::internal::TypeParameterizedTest< \
CaseName, \
::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)>, \
GTEST_TYPE_PARAMS_( \
CaseName)>::Register("", \
::testing::internal::CodeLocation( \
__FILE__, __LINE__), \
GTEST_STRINGIFY_(CaseName), \
GTEST_STRINGIFY_(TestName), 0, \
::testing::internal::GenerateNames< \
GTEST_NAME_GENERATOR_(CaseName), \
GTEST_TYPE_PARAMS_(CaseName)>()); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)<gtest_TypeParam_>::TestBody()
// Legacy API is deprecated but still available
@@ -268,23 +267,22 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define TYPED_TEST_P(SuiteName, TestName) \
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
template <typename gtest_TypeParam_> \
class TestName : public SuiteName<gtest_TypeParam_> { \
private: \
typedef SuiteName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \
gtest_##TestName##_defined_ = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
__FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \
GTEST_STRINGIFY_(TestName)); \
} \
template <typename gtest_TypeParam_> \
void GTEST_SUITE_NAMESPACE_( \
#define TYPED_TEST_P(SuiteName, TestName) \
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
template <typename gtest_TypeParam_> \
class TestName : public SuiteName<gtest_TypeParam_> { \
private: \
typedef SuiteName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
[[maybe_unused]] static bool gtest_##TestName##_defined_ = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
__FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \
GTEST_STRINGIFY_(TestName)); \
} \
template <typename gtest_TypeParam_> \
void GTEST_SUITE_NAMESPACE_( \
SuiteName)::TestName<gtest_TypeParam_>::TestBody()
// Note: this won't work correctly if the trailing arguments are macros.
@@ -292,8 +290,8 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \
} \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static const char* const \
GTEST_REGISTERED_TEST_NAMES_(SuiteName) = \
[[maybe_unused]] static const char* const GTEST_REGISTERED_TEST_NAMES_( \
SuiteName) = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
@@ -305,24 +303,22 @@ INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
REGISTER_TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \
"test-suit-prefix must not be empty"); \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static bool \
gtest_##Prefix##_##SuiteName = \
::testing::internal::TypeParameterizedTestSuite< \
SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
::testing::internal::GenerateTypeList<Types>::type>:: \
Register( \
GTEST_STRINGIFY_(Prefix), \
::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \
GTEST_STRINGIFY_(SuiteName), \
GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
::testing::internal::GenerateNames< \
::testing::internal::NameGeneratorSelector< \
__VA_ARGS__>::type, \
::testing::internal::GenerateTypeList<Types>::type>())
#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \
"test-suit-prefix must not be empty"); \
[[maybe_unused]] static bool gtest_##Prefix##_##SuiteName = \
::testing::internal::TypeParameterizedTestSuite< \
SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
::testing::internal::GenerateTypeList<Types>::type>:: \
Register(GTEST_STRINGIFY_(Prefix), \
::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \
GTEST_STRINGIFY_(SuiteName), \
GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
::testing::internal::GenerateNames< \
::testing::internal::NameGeneratorSelector< \
__VA_ARGS__>::type, \
::testing::internal::GenerateTypeList<Types>::type>())
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_

View File

@@ -1123,7 +1123,7 @@ class GTEST_API_ UnitTest {
// This method can only be called from the main thread.
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
int Run() GTEST_MUST_USE_RESULT_;
[[nodiscard]] int Run();
// Returns the working directory when the first TEST() or TEST_F()
// was executed. The UnitTest object owns the string.
@@ -2329,7 +2329,7 @@ TestInfo* RegisterTest(const char* test_suite_name, const char* test_name,
//
// This function was formerly a macro; thus, it is in the global
// namespace and has an all-caps name.
int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_;
[[nodiscard]] int RUN_ALL_TESTS();
inline int RUN_ALL_TESTS() { return ::testing::UnitTest::GetInstance()->Run(); }

View File

@@ -290,17 +290,17 @@ class FloatingPoint {
// around may change its bits, although the new value is guaranteed
// to be also a NAN. Therefore, don't expect this constructor to
// preserve the bits in x when x is a NAN.
explicit FloatingPoint(const RawType& x) { u_.value_ = x; }
explicit FloatingPoint(RawType x) { memcpy(&bits_, &x, sizeof(x)); }
// Static methods
// Reinterprets a bit pattern as a floating-point number.
//
// This function is needed to test the AlmostEquals() method.
static RawType ReinterpretBits(const Bits bits) {
FloatingPoint fp(0);
fp.u_.bits_ = bits;
return fp.u_.value_;
static RawType ReinterpretBits(Bits bits) {
RawType fp;
memcpy(&fp, &bits, sizeof(fp));
return fp;
}
// Returns the floating-point number that represent positive infinity.
@@ -309,16 +309,16 @@ class FloatingPoint {
// Non-static methods
// Returns the bits that represents this number.
const Bits& bits() const { return u_.bits_; }
const Bits& bits() const { return bits_; }
// Returns the exponent bits of this number.
Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }
Bits exponent_bits() const { return kExponentBitMask & bits_; }
// Returns the fraction bits of this number.
Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }
Bits fraction_bits() const { return kFractionBitMask & bits_; }
// Returns the sign bit of this number.
Bits sign_bit() const { return kSignBitMask & u_.bits_; }
Bits sign_bit() const { return kSignBitMask & bits_; }
// Returns true if and only if this is NAN (not a number).
bool is_nan() const {
@@ -332,23 +332,16 @@ class FloatingPoint {
//
// - returns false if either number is (or both are) NAN.
// - treats really large numbers as almost equal to infinity.
// - thinks +0.0 and -0.0 are 0 DLP's apart.
// - thinks +0.0 and -0.0 are 0 ULP's apart.
bool AlmostEquals(const FloatingPoint& rhs) const {
// The IEEE standard says that any comparison operation involving
// a NAN must return false.
if (is_nan() || rhs.is_nan()) return false;
return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) <=
kMaxUlps;
return DistanceBetweenSignAndMagnitudeNumbers(bits_, rhs.bits_) <= kMaxUlps;
}
private:
// The data type used to store the actual floating-point number.
union FloatingPointUnion {
RawType value_; // The raw floating-point number.
Bits bits_; // The bits that represent the number.
};
// Converts an integer from the sign-and-magnitude representation to
// the biased representation. More precisely, let N be 2 to the
// power of (kBitCount - 1), an integer x is represented by the
@@ -364,7 +357,7 @@ class FloatingPoint {
//
// Read https://en.wikipedia.org/wiki/Signed_number_representations
// for more details on signed number representations.
static Bits SignAndMagnitudeToBiased(const Bits& sam) {
static Bits SignAndMagnitudeToBiased(Bits sam) {
if (kSignBitMask & sam) {
// sam represents a negative number.
return ~sam + 1;
@@ -376,14 +369,13 @@ class FloatingPoint {
// Given two numbers in the sign-and-magnitude representation,
// returns the distance between them as an unsigned number.
static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits& sam1,
const Bits& sam2) {
static Bits DistanceBetweenSignAndMagnitudeNumbers(Bits sam1, Bits sam2) {
const Bits biased1 = SignAndMagnitudeToBiased(sam1);
const Bits biased2 = SignAndMagnitudeToBiased(sam2);
return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
}
FloatingPointUnion u_;
Bits bits_; // The bits that represent the number.
};
// Typedefs the instances of the FloatingPoint template class that we
@@ -894,11 +886,6 @@ class HasDebugStringAndShortDebugString {
HasDebugStringType::value && HasShortDebugStringType::value;
};
#ifdef GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
template <typename T>
constexpr bool HasDebugStringAndShortDebugString<T>::value;
#endif
// When the compiler sees expression IsContainerTest<C>(0), if C is an
// STL-style container class, the first overload of IsContainerTest
// will be viable (since both C::iterator* and C::const_iterator* are
@@ -1241,30 +1228,40 @@ class FlatTuple
// Utility functions to be called with static_assert to induce deprecation
// warnings.
GTEST_INTERNAL_DEPRECATED(
[[deprecated(
"INSTANTIATE_TEST_CASE_P is deprecated, please use "
"INSTANTIATE_TEST_SUITE_P")
constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; }
"INSTANTIATE_TEST_SUITE_P")]]
constexpr bool InstantiateTestCase_P_IsDeprecated() {
return true;
}
GTEST_INTERNAL_DEPRECATED(
[[deprecated(
"TYPED_TEST_CASE_P is deprecated, please use "
"TYPED_TEST_SUITE_P")
constexpr bool TypedTestCase_P_IsDeprecated() { return true; }
"TYPED_TEST_SUITE_P")]]
constexpr bool TypedTestCase_P_IsDeprecated() {
return true;
}
GTEST_INTERNAL_DEPRECATED(
[[deprecated(
"TYPED_TEST_CASE is deprecated, please use "
"TYPED_TEST_SUITE")
constexpr bool TypedTestCaseIsDeprecated() { return true; }
"TYPED_TEST_SUITE")]]
constexpr bool TypedTestCaseIsDeprecated() {
return true;
}
GTEST_INTERNAL_DEPRECATED(
[[deprecated(
"REGISTER_TYPED_TEST_CASE_P is deprecated, please use "
"REGISTER_TYPED_TEST_SUITE_P")
constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; }
"REGISTER_TYPED_TEST_SUITE_P")]]
constexpr bool RegisterTypedTestCase_P_IsDeprecated() {
return true;
}
GTEST_INTERNAL_DEPRECATED(
[[deprecated(
"INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use "
"INSTANTIATE_TYPED_TEST_SUITE_P")
constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; }
"INSTANTIATE_TYPED_TEST_SUITE_P")]]
constexpr bool InstantiateTypedTestCase_P_IsDeprecated() {
return true;
}
} // namespace internal
} // namespace testing
@@ -1501,8 +1498,7 @@ class NeverThrown {
\
private: \
void TestBody() override; \
GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED static ::testing::TestInfo* const \
test_info_; \
[[maybe_unused]] static ::testing::TestInfo* const test_info_; \
}; \
\
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \

View File

@@ -39,6 +39,7 @@
#include <ctype.h>
#include <cassert>
#include <functional>
#include <iterator>
#include <map>
#include <memory>
@@ -529,8 +530,7 @@ class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
// prefix). test_base_name is the name of an individual test without
// parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
// test suite base name and DoBar is test base name.
void AddTestPattern(const char*,
const char* test_base_name,
void AddTestPattern(const char*, const char* test_base_name,
TestMetaFactoryBase<ParamType>* meta_factory,
CodeLocation code_location) {
tests_.emplace_back(
@@ -952,11 +952,11 @@ class CartesianProductHolder {
std::tuple<Gen...> generators_;
};
template <typename From, typename To>
template <typename From, typename To, typename Func>
class ParamGeneratorConverter : public ParamGeneratorInterface<To> {
public:
ParamGeneratorConverter(ParamGenerator<From> gen) // NOLINT
: generator_(std::move(gen)) {}
ParamGeneratorConverter(ParamGenerator<From> gen, Func converter) // NOLINT
: generator_(std::move(gen)), converter_(std::move(converter)) {}
ParamIteratorInterface<To>* Begin() const override {
return new Iterator(this, generator_.begin(), generator_.end());
@@ -965,13 +965,21 @@ class ParamGeneratorConverter : public ParamGeneratorInterface<To> {
return new Iterator(this, generator_.end(), generator_.end());
}
// Returns the std::function wrapping the user-supplied converter callable. It
// is used by the iterator (see class Iterator below) to convert the object
// (of type FROM) returned by the ParamGenerator to an object of a type that
// can be static_cast to type TO.
const Func& TypeConverter() const { return converter_; }
private:
class Iterator : public ParamIteratorInterface<To> {
public:
Iterator(const ParamGeneratorInterface<To>* base, ParamIterator<From> it,
Iterator(const ParamGeneratorConverter* base, ParamIterator<From> it,
ParamIterator<From> end)
: base_(base), it_(it), end_(end) {
if (it_ != end_) value_ = std::make_shared<To>(static_cast<To>(*it_));
if (it_ != end_)
value_ =
std::make_shared<To>(static_cast<To>(base->TypeConverter()(*it_)));
}
~Iterator() override = default;
@@ -980,7 +988,9 @@ class ParamGeneratorConverter : public ParamGeneratorInterface<To> {
}
void Advance() override {
++it_;
if (it_ != end_) value_ = std::make_shared<To>(static_cast<To>(*it_));
if (it_ != end_)
value_ =
std::make_shared<To>(static_cast<To>(base_->TypeConverter()(*it_)));
}
ParamIteratorInterface<To>* Clone() const override {
return new Iterator(*this);
@@ -1000,30 +1010,54 @@ class ParamGeneratorConverter : public ParamGeneratorInterface<To> {
private:
Iterator(const Iterator& other) = default;
const ParamGeneratorInterface<To>* const base_;
const ParamGeneratorConverter* const base_;
ParamIterator<From> it_;
ParamIterator<From> end_;
std::shared_ptr<To> value_;
}; // class ParamGeneratorConverter::Iterator
ParamGenerator<From> generator_;
Func converter_;
}; // class ParamGeneratorConverter
template <class Gen>
template <class GeneratedT,
typename StdFunction =
std::function<const GeneratedT&(const GeneratedT&)>>
class ParamConverterGenerator {
public:
ParamConverterGenerator(ParamGenerator<Gen> g) // NOLINT
: generator_(std::move(g)) {}
ParamConverterGenerator(ParamGenerator<GeneratedT> g) // NOLINT
: generator_(std::move(g)), converter_(Identity) {}
ParamConverterGenerator(ParamGenerator<GeneratedT> g, StdFunction converter)
: generator_(std::move(g)), converter_(std::move(converter)) {}
template <typename T>
operator ParamGenerator<T>() const { // NOLINT
return ParamGenerator<T>(new ParamGeneratorConverter<Gen, T>(generator_));
return ParamGenerator<T>(
new ParamGeneratorConverter<GeneratedT, T, StdFunction>(generator_,
converter_));
}
private:
ParamGenerator<Gen> generator_;
static const GeneratedT& Identity(const GeneratedT& v) { return v; }
ParamGenerator<GeneratedT> generator_;
StdFunction converter_;
};
// Template to determine the param type of a single-param std::function.
template <typename T>
struct FuncSingleParamType;
template <typename R, typename P>
struct FuncSingleParamType<std::function<R(P)>> {
using type = std::remove_cv_t<std::remove_reference_t<P>>;
};
template <typename T>
struct IsSingleArgStdFunction : public std::false_type {};
template <typename R, typename P>
struct IsSingleArgStdFunction<std::function<R(P)>> : public std::true_type {};
} // namespace internal
} // namespace testing

View File

@@ -194,7 +194,6 @@
//
// Macros for basic C++ coding:
// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used.
// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is
// suppressed (constant conditional).
// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127
@@ -260,11 +259,6 @@
// BoolFromGTestEnv() - parses a bool environment variable.
// Int32FromGTestEnv() - parses an int32_t environment variable.
// StringFromGTestEnv() - parses a string environment variable.
//
// Deprecation warnings:
// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as
// deprecated; calling a marked function
// should generate a compiler warning
// The definition of GTEST_INTERNAL_CPLUSPLUS_LANG comes first because it can
// potentially be used as an #include guard.
@@ -275,8 +269,8 @@
#endif
#if !defined(GTEST_INTERNAL_CPLUSPLUS_LANG) || \
GTEST_INTERNAL_CPLUSPLUS_LANG < 201402L
#error C++ versions less than C++14 are not supported.
GTEST_INTERNAL_CPLUSPLUS_LANG < 201703L
#error C++ versions less than C++17 are not supported.
#endif
// MSVC >= 19.11 (VS 2017 Update 3) supports __has_include.
@@ -288,10 +282,14 @@
// Detect C++ feature test macros as gracefully as possible.
// MSVC >= 19.15, Clang >= 3.4.1, and GCC >= 4.1.2 support feature test macros.
#if GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L && \
(!defined(__has_include) || GTEST_INTERNAL_HAS_INCLUDE(<version>))
#include <version> // C++20 and later
#elif (!defined(__has_include) || GTEST_INTERNAL_HAS_INCLUDE(<ciso646>))
//
// GCC15 warns that <ciso646> is deprecated in C++17 and suggests using
// <version> instead, even though <version> is not available in C++17 mode prior
// to GCC9.
#if GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L || \
GTEST_INTERNAL_HAS_INCLUDE(<version>)
#include <version> // C++20 or <version> support.
#else
#include <ciso646> // Pre-C++20
#endif
@@ -772,25 +770,6 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
#define GTEST_HAVE_FEATURE_(x) 0
#endif
// Use this annotation after a variable or parameter declaration to tell the
// compiler the variable/parameter may be used.
// Example:
//
// GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED int foo = bar();
//
// This can be removed once we only support only C++17 or newer and
// [[maybe_unused]] is available on all supported platforms.
#if GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(maybe_unused)
#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
#elif GTEST_HAVE_ATTRIBUTE_(unused)
// This is inferior to [[maybe_unused]] as it can produce a
// -Wused-but-marked-unused warning on optionally used symbols, but it is all we
// have.
#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED __attribute__((__unused__))
#else
#define GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED
#endif
// Use this annotation before a function that takes a printf format string.
#if GTEST_HAVE_ATTRIBUTE_(format) && defined(__MINGW_PRINTF_FORMAT)
// MinGW has two different printf implementations. Ensure the format macro
@@ -805,17 +784,6 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check)
#endif
// Tell the compiler to warn about unused return values for functions declared
// with this macro. The macro should be used on function declarations
// following the argument list:
//
// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_;
#if GTEST_HAVE_ATTRIBUTE_(warn_unused_result)
#define GTEST_MUST_USE_RESULT_ __attribute__((warn_unused_result))
#else
#define GTEST_MUST_USE_RESULT_
#endif
// MS C++ compiler emits warning when a conditional expression is compile time
// constant. In some contexts this warning is false positive and needs to be
// suppressed. Use the following two macros in such cases:
@@ -2367,26 +2335,6 @@ const char* StringFromGTestEnv(const char* flag, const char* default_val);
} // namespace internal
} // namespace testing
#if !defined(GTEST_INTERNAL_DEPRECATED)
// Internal Macro to mark an API deprecated, for googletest usage only
// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or
// GTEST_INTERNAL_DEPRECATED(message) <return_type> myFunction(); Every usage of
// a deprecated entity will trigger a warning when compiled with
// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler).
// For msvc /W3 option will need to be used
// Note that for 'other' compilers this macro evaluates to nothing to prevent
// compilations errors.
#if defined(_MSC_VER)
#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message))
#elif defined(__GNUC__)
#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message)))
#else
#define GTEST_INTERNAL_DEPRECATED(message)
#endif
#endif // !defined(GTEST_INTERNAL_DEPRECATED)
#ifdef GTEST_HAS_ABSL
// Always use absl::any for UniversalPrinter<> specializations if googletest
// is built with absl support.
@@ -2527,12 +2475,6 @@ using Variant = ::std::variant<T...>;
#define GTEST_INTERNAL_HAS_VARIANT 0
#endif
#if (defined(__cpp_constexpr) && !defined(__cpp_inline_variables)) || \
(defined(GTEST_INTERNAL_CPLUSPLUS_LANG) && \
GTEST_INTERNAL_CPLUSPLUS_LANG < 201703L)
#define GTEST_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1
#endif
#if (defined(__cpp_lib_three_way_comparison) || \
(GTEST_INTERNAL_HAS_INCLUDE(<compare>) && \
GTEST_INTERNAL_CPLUSPLUS_LANG >= 201907L))

View File

@@ -826,6 +826,10 @@ class GTEST_API_ UnitTestImpl {
bool catch_exceptions() const { return catch_exceptions_; }
private:
// Returns true if a warning should be issued if no tests match the test
// filter flag.
bool ShouldWarnIfNoTestsMatchFilter() const;
struct CompareTestSuitesByPointer {
bool operator()(const TestSuite* lhs, const TestSuite* rhs) const {
return lhs->name_ < rhs->name_;

View File

@@ -192,12 +192,17 @@ static const char kDefaultOutputFormat[] = "xml";
// The default output file.
static const char kDefaultOutputFile[] = "test_detail";
// These environment variables are set by Bazel.
// https://bazel.build/reference/test-encyclopedia#initial-conditions
//
// The environment variable name for the test shard index.
static const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
// The environment variable name for the total number of test shards.
static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS";
// The environment variable name for the test shard status file.
static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE";
// The environment variable name for the test output warnings file.
static const char kTestWarningsOutputFile[] = "TEST_WARNINGS_OUTPUT_FILE";
namespace internal {
@@ -258,6 +263,12 @@ GTEST_DEFINE_bool_(
testing::GetDefaultFailFast()),
"True if and only if a test failure should stop further test execution.");
GTEST_DEFINE_bool_(
fail_if_no_test_linked,
testing::internal::BoolFromGTestEnv("fail_if_no_test_linked", false),
"True if and only if the test should fail if no test case (including "
"disabled test cases) is linked.");
GTEST_DEFINE_bool_(
also_run_disabled_tests,
testing::internal::BoolFromGTestEnv("also_run_disabled_tests", false),
@@ -5869,6 +5880,23 @@ TestSuite* UnitTestImpl::GetTestSuite(
static void SetUpEnvironment(Environment* env) { env->SetUp(); }
static void TearDownEnvironment(Environment* env) { env->TearDown(); }
// If the environment variable TEST_WARNINGS_OUTPUT_FILE was provided, appends
// `str` to the file, creating the file if necessary.
#if GTEST_HAS_FILE_SYSTEM
static void AppendToTestWarningsOutputFile(const std::string& str) {
const char* const filename = posix::GetEnv(kTestWarningsOutputFile);
if (filename == nullptr) {
return;
}
auto* const file = posix::FOpen(filename, "a");
if (file == nullptr) {
return;
}
GTEST_CHECK_(fwrite(str.data(), 1, str.size(), file) == str.size());
GTEST_CHECK_(posix::FClose(file) == 0);
}
#endif // GTEST_HAS_FILE_SYSTEM
// Runs all tests in this UnitTest object, prints the result, and
// returns true if all tests are successful. If any exception is
// thrown during a test, the test is considered to be failed, but the
@@ -5890,6 +5918,28 @@ bool UnitTestImpl::RunAllTests() {
// user didn't call InitGoogleTest.
PostFlagParsingInit();
// Handle the case where the program has no tests linked.
// Sometimes this is a programmer mistake, but sometimes it is intended.
if (total_test_count() == 0) {
constexpr char kNoTestLinkedMessage[] =
"This test program does NOT link in any test case.";
constexpr char kNoTestLinkedFatal[] =
"This is INVALID. Please make sure to link in at least one test case.";
constexpr char kNoTestLinkedWarning[] =
"Please make sure this is intended.";
const bool fail_if_no_test_linked = GTEST_FLAG_GET(fail_if_no_test_linked);
ColoredPrintf(
GTestColor::kRed, "%s %s\n", kNoTestLinkedMessage,
fail_if_no_test_linked ? kNoTestLinkedFatal : kNoTestLinkedWarning);
if (fail_if_no_test_linked) {
return false;
}
#if GTEST_HAS_FILE_SYSTEM
AppendToTestWarningsOutputFile(std::string(kNoTestLinkedMessage) + ' ' +
kNoTestLinkedWarning + '\n');
#endif // GTEST_HAS_FILE_SYSTEM
}
#if GTEST_HAS_FILE_SYSTEM
// Even if sharding is not on, test runners may want to use the
// GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding
@@ -6063,6 +6113,17 @@ bool UnitTestImpl::RunAllTests() {
environments_.clear();
}
// Try to warn the user if no tests matched the test filter.
if (ShouldWarnIfNoTestsMatchFilter()) {
const std::string filter_warning =
std::string("filter \"") + GTEST_FLAG_GET(filter) +
"\" did not match any test; no tests were run\n";
ColoredPrintf(GTestColor::kRed, "WARNING: %s", filter_warning.c_str());
#if GTEST_HAS_FILE_SYSTEM
AppendToTestWarningsOutputFile(filter_warning);
#endif // GTEST_HAS_FILE_SYSTEM
}
if (!gtest_is_initialized_before_run_all_tests) {
ColoredPrintf(
GTestColor::kRed,
@@ -6231,6 +6292,30 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
return num_selected_tests;
}
// Returns true if a warning should be issued if no tests match the test filter
// flag. We can't simply count the number of tests that ran because, for
// instance, test sharding and death tests might mean no tests are expected to
// run in this process, but will run in another process.
bool UnitTestImpl::ShouldWarnIfNoTestsMatchFilter() const {
if (total_test_count() == 0) {
// No tests were linked in to program.
// This case is handled by a different warning.
return false;
}
const PositiveAndNegativeUnitTestFilter gtest_flag_filter(
GTEST_FLAG_GET(filter));
for (auto* test_suite : test_suites_) {
const std::string& test_suite_name = test_suite->name_;
for (TestInfo* test_info : test_suite->test_info_list()) {
const std::string& test_name = test_info->name_;
if (gtest_flag_filter.MatchesTest(test_suite_name, test_name)) {
return false;
}
}
}
return true;
}
// Prints the given C-string on a single line by replacing all '\n'
// characters with string "\\n". If the output takes more than
// max_length characters, only prints the first max_length characters
@@ -6677,6 +6762,7 @@ static bool ParseGoogleTestFlag(const char* const arg) {
GTEST_INTERNAL_PARSE_FLAG(death_test_style);
GTEST_INTERNAL_PARSE_FLAG(death_test_use_fork);
GTEST_INTERNAL_PARSE_FLAG(fail_fast);
GTEST_INTERNAL_PARSE_FLAG(fail_if_no_test_linked);
GTEST_INTERNAL_PARSE_FLAG(filter);
GTEST_INTERNAL_PARSE_FLAG(internal_run_death_test);
GTEST_INTERNAL_PARSE_FLAG(list_tests);

View File

@@ -129,6 +129,10 @@
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,9 @@
#ifndef IMGUI_DISABLE
// Usage:
// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to enable support for imgui_freetype in imgui.
// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support
// for imgui_freetype in imgui. It is equivalent to selecting the default loader with:
// io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())
// Optional support for OpenType SVG fonts:
// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927.
@@ -14,44 +16,67 @@
// Forward declarations
struct ImFontAtlas;
struct ImFontBuilderIO;
struct ImFontLoader;
// Hinting greatly impacts visuals (and glyph sizes).
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
enum ImGuiFreeTypeBuilderFlags
// You can set those flags globally in ImFontAtlas::FontLoaderFlags
// You can set those flags on a per font basis in ImFontConfig::FontLoaderFlags
typedef unsigned int ImGuiFreeTypeLoaderFlags;
enum ImGuiFreeTypeLoaderFlags_
{
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 // Enable FreeType bitmap glyphs
ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeLoaderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9, // Enable FreeType bitmap glyphs
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiFreeTypeBuilderFlags_NoHinting = ImGuiFreeTypeLoaderFlags_NoHinting,
ImGuiFreeTypeBuilderFlags_NoAutoHint = ImGuiFreeTypeLoaderFlags_NoAutoHint,
ImGuiFreeTypeBuilderFlags_ForceAutoHint = ImGuiFreeTypeLoaderFlags_ForceAutoHint,
ImGuiFreeTypeBuilderFlags_LightHinting = ImGuiFreeTypeLoaderFlags_LightHinting,
ImGuiFreeTypeBuilderFlags_MonoHinting = ImGuiFreeTypeLoaderFlags_MonoHinting,
ImGuiFreeTypeBuilderFlags_Bold = ImGuiFreeTypeLoaderFlags_Bold,
ImGuiFreeTypeBuilderFlags_Oblique = ImGuiFreeTypeLoaderFlags_Oblique,
ImGuiFreeTypeBuilderFlags_Monochrome = ImGuiFreeTypeLoaderFlags_Monochrome,
ImGuiFreeTypeBuilderFlags_LoadColor = ImGuiFreeTypeLoaderFlags_LoadColor,
ImGuiFreeTypeBuilderFlags_Bitmap = ImGuiFreeTypeLoaderFlags_Bitmap,
#endif
};
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_;
#endif
namespace ImGuiFreeType
{
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
// If you need to dynamically select between multiple builders:
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
// - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())'
// - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data.
IMGUI_API const ImFontLoader* GetFontLoader();
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr);
// Obsolete names (will be removed soon)
// Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source)
IMGUI_API bool DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags);
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
//IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection.
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
#endif
}

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.91.9b
// dear imgui, v1.92.1
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@@ -37,6 +37,7 @@ Index of this file:
// [SECTION] Tab bar, Tab item support
// [SECTION] Table support
// [SECTION] ImGui internal API
// [SECTION] ImFontLoader
// [SECTION] ImFontAtlas internal API
// [SECTION] Test Engine specific hooks (imgui_test_engine)
@@ -132,7 +133,7 @@ Index of this file:
//-----------------------------------------------------------------------------
// Utilities
// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImPool<>, ImChunkStream<>)
// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImStableVector<>, ImPool<>, ImChunkStream<>)
struct ImBitVector; // Store 1-bit per value
struct ImRect; // An axis-aligned rectangle (2 points)
struct ImGuiTextIndex; // Maintain a line index for a text buffer.
@@ -140,6 +141,9 @@ struct ImGuiTextIndex; // Maintain a line index for a text buffer.
// ImDrawList/ImFontAtlas
struct ImDrawDataBuilder; // Helper to build a ImDrawData instance
struct ImDrawListSharedData; // Data shared between all ImDrawList instances
struct ImFontAtlasBuilder; // Internal storage for incrementally packing and building a ImFontAtlas
struct ImFontAtlasPostProcessData; // Data available to potential texture post-processing functions
struct ImFontAtlasRectEntry; // Packed rectangle lookup entry
// ImGui
struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others)
@@ -206,6 +210,10 @@ typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // F
typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest()
typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
// Table column indexing
typedef ImS16 ImGuiTableColumnIdx;
typedef ImU16 ImGuiTableDrawChannelIdx;
//-----------------------------------------------------------------------------
// [SECTION] Context pointer
// See implementation of this variable in imgui.cpp for comments and details.
@@ -238,7 +246,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FONT(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FONT(...) do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context.
#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
// Static Asserts
@@ -348,6 +356,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
// - Helper: ImBitArray
// - Helper: ImBitVector
// - Helper: ImSpan<>, ImSpanAllocator<>
// - Helper: ImStableVector<>
// - Helper: ImPool<>
// - Helper: ImChunkStream<>
// - Helper: ImGuiTextIndex
@@ -379,6 +388,7 @@ IMGUI_API int ImStricmp(const char* str1, const char* str2);
IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); // Case insensitive compare to a certain count.
IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); // Copy to a certain count and always zero terminate (strncpy doesn't).
IMGUI_API char* ImStrdup(const char* str); // Duplicate a string.
IMGUI_API void* ImMemdup(const void* src, size_t size); // Duplicate a chunk of memory.
IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed.
IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); // Find first occurrence of 'c' in string range.
IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line
@@ -492,6 +502,8 @@ static inline float ImTrunc(float f)
static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
static inline float ImTrunc64(float f) { return (float)(ImS64)(f); }
static inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); }
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
@@ -524,6 +536,14 @@ struct ImVec1
constexpr ImVec1(float _x) : x(_x) { }
};
// Helper: ImVec2i (2D vector, integer)
struct ImVec2i
{
int x, y;
constexpr ImVec2i() : x(0), y(0) {}
constexpr ImVec2i(int _x, int _y) : x(_x), y(_y) {}
};
// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)
struct ImVec2ih
{
@@ -675,6 +695,39 @@ struct ImSpanAllocator
inline void GetSpan(int n, ImSpan<T>* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }
};
// Helper: ImStableVector<>
// Allocating chunks of BLOCK_SIZE items. Objects pointers are never invalidated when growing, only by clear().
// Important: does not destruct anything!
// Implemented only the minimum set of functions we need for it.
template<typename T, int BLOCK_SIZE>
struct ImStableVector
{
int Size = 0;
int Capacity = 0;
ImVector<T*> Blocks;
// Functions
inline ~ImStableVector() { for (T* block : Blocks) IM_FREE(block); }
inline void clear() { Size = Capacity = 0; Blocks.clear_delete(); }
inline void resize(int new_size) { if (new_size > Capacity) reserve(new_size); Size = new_size; }
inline void reserve(int new_cap)
{
new_cap = IM_MEMALIGN(new_cap, BLOCK_SIZE);
int old_count = Capacity / BLOCK_SIZE;
int new_count = new_cap / BLOCK_SIZE;
if (new_count <= old_count)
return;
Blocks.resize(new_count);
for (int n = old_count; n < new_count; n++)
Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCK_SIZE);
Capacity = new_cap;
}
inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
inline T* push_back(const T& v) { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCK_SIZE); void* ptr = &Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; }
};
// Helper: ImPool<>
// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
@@ -783,17 +836,20 @@ IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStorag
// You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
struct IMGUI_API ImDrawListSharedData
{
ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
ImFont* Font; // Current/default font (optional, for simplified AddText overload)
float FontSize; // Current/default font size (optional, for simplified AddText overload)
float FontScale; // Current/default font scale (== FontSize / Font->FontSize)
ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel)
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas (== FontAtlas->TexUvLines)
ImFontAtlas* FontAtlas; // Current font atlas
ImFont* Font; // Current font (used for simplified AddText overload)
float FontSize; // Current font size (used for for simplified AddText overload)
float FontScale; // Current font scale (== FontSize / Font->FontSize)
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo()
float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
float InitialFringeScale; // Initial scale to apply to AA fringe
ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
ImVector<ImVec2> TempBuffer; // Temporary write buffer
ImVector<ImDrawList*> DrawLists; // All draw lists associated to this ImDrawListSharedData
ImGuiContext* Context; // [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case.
// Lookup tables
ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
@@ -801,6 +857,7 @@ struct IMGUI_API ImDrawListSharedData
ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
ImDrawListSharedData();
~ImDrawListSharedData();
void SetCircleTessellationMaxError(float max_error);
};
@@ -812,6 +869,13 @@ struct ImDrawDataBuilder
ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); }
};
struct ImFontStackData
{
ImFont* Font;
float FontSizeBeforeScaling; // ~~ style.FontSizeBase
float FontSizeAfterScaling; // ~~ g.FontSize
};
//-----------------------------------------------------------------------------
// [SECTION] Style support
//-----------------------------------------------------------------------------
@@ -883,6 +947,7 @@ enum ImGuiItemFlagsPrivate_
ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15, // false // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false).
ImGuiItemFlags_NoMarkEdited = 1 << 16, // false // Skip calling MarkItemEdited()
ImGuiItemFlags_NoFocus = 1 << 17, // false // [EXPERIMENTAL: Not very well specced] Clicking doesn't take focus. Automatically sets ImGuiButtonFlags_NoFocus + ImGuiButtonFlags_NoNavFocus in ButtonBehavior().
// Controlled by widget code
ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
@@ -911,6 +976,7 @@ enum ImGuiItemStatusFlags_
ImGuiItemStatusFlags_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).
ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid.
ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().
//ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb.
// Additional status + semantic for ImGuiTestEngine
#ifdef IMGUI_ENABLE_TEST_ENGINE
@@ -960,8 +1026,10 @@ enum ImGuiButtonFlagsPrivate_
ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item
ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
ImGuiButtonFlags_NoFocus = 1 << 22, // [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking.
ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,
ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease,
//ImGuiButtonFlags_NoKeyModifiers = ImGuiButtonFlags_NoKeyModsAllowed, // Renamed in 1.91.4
};
// Extend ImGuiComboFlags_
@@ -994,9 +1062,11 @@ enum ImGuiSelectableFlagsPrivate_
// Extend ImGuiTreeNodeFlags_
enum ImGuiTreeNodeFlagsPrivate_
{
ImGuiTreeNodeFlags_NoNavFocus = 1 << 27,// Don't claim nav focus when interacting with this item (#8551)
ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()
ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517)
ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,
ImGuiTreeNodeFlags_DrawLinesMask_ = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes,
};
enum ImGuiSeparatorFlags_
@@ -1283,15 +1353,18 @@ struct ImGuiLastItemData
};
// Store data emitted by TreeNode() for usage by TreePop()
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere: store the minimum amount of data
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsToParent: store the minimum amount of data
// which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult().
// Only stored when the node is a potential candidate for landing on a Left arrow jump.
struct ImGuiTreeNodeStackData
{
ImGuiID ID;
ImGuiTreeNodeFlags TreeFlags;
ImGuiItemFlags ItemFlags; // Used for nav landing
ImRect NavRect; // Used for nav landing
ImGuiItemFlags ItemFlags; // Used for nav landing
ImRect NavRect; // Used for nav landing
float DrawLinesX1;
float DrawLinesToNodesY2;
ImGuiTableColumnIdx DrawLinesTableColumn;
};
// sizeof() = 20
@@ -1607,6 +1680,7 @@ enum ImGuiNavRenderCursorFlags_
ImGuiNavHighlightFlags_Compact = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.91.4
ImGuiNavHighlightFlags_AlwaysDraw = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4
ImGuiNavHighlightFlags_NoRounding = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4
//ImGuiNavHighlightFlags_TypeThin = ImGuiNavRenderCursorFlags_Compact, // Renamed in 1.90.2
#endif
};
@@ -2007,10 +2081,12 @@ struct ImGuiMetricsConfig
bool ShowDrawCmdMesh = true;
bool ShowDrawCmdBoundingBoxes = true;
bool ShowTextEncodingViewer = false;
bool ShowTextureUsedRect = false;
int ShowWindowsRectsType = -1;
int ShowTablesRectsType = -1;
int HighlightMonitorIdx = -1;
ImGuiID HighlightViewportID = 0;
bool ShowFontPreview = true;
};
struct ImGuiStackLevelInfo
@@ -2063,15 +2139,17 @@ struct ImGuiContextHook
struct ImGuiContext
{
bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO;
ImGuiPlatformIO PlatformIO;
ImGuiStyle Style;
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height.
float FontScale; // == FontSize / Font->FontSize
float CurrentDpiScale; // Current window/viewport DpiScale
ImVector<ImFontAtlas*> FontAtlases; // List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas)
ImFont* Font; // Currently bound font. (== FontStack.back().Font)
ImFontBaked* FontBaked; // Currently bound font at currently bound size. (== Font->GetFontBaked(FontSize))
float FontSize; // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function).
float FontSizeBase; // Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified.
float FontBakedScale; // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f.
float FontRasterizerDensity; // Current font density. Used by all calls to GetFontBaked().
float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale
ImDrawListSharedData DrawListSharedData;
double Time;
int FrameCount;
@@ -2114,7 +2192,7 @@ struct ImGuiContext
ImVec2 WheelingAxisAvg;
// Item/widgets state and tracking information
ImGuiID DebugDrawIdConflicts; // Set when we detect multiple items with the same identifier
ImGuiID DebugDrawIdConflictsId; // Set when we detect multiple items with the same identifier
ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
@@ -2172,7 +2250,7 @@ struct ImGuiContext
ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line)
ImVector<ImGuiColorMod> ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
ImVector<ImGuiStyleMod> StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
ImVector<ImFontStackData> FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
ImVector<ImGuiFocusScopeData> FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
ImVector<ImGuiItemFlags> ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
ImVector<ImGuiGroupData> GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
@@ -2194,18 +2272,19 @@ struct ImGuiContext
ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow'
ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope)
ImGuiNavLayer NavLayer; // Focused layer (main scrolling layer, or menu/title bar layer)
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem()
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID()
ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0
ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)
ImGuiActivateFlags NavActivateFlags;
ImVector<ImGuiFocusScopeData> NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain.
ImGuiID NavHighlightActivatedId;
float NavHighlightActivatedTimer;
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame.
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
ImS8 NavCursorHideFrames;
//ImGuiID NavActivateInputId; // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606.
// Navigation: Init & Move Requests
bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
@@ -2240,6 +2319,7 @@ struct ImGuiContext
bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
bool ConfigNavWindowingWithGamepad; // = true. Enable CTRL+TAB by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer.
ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)
ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
@@ -2247,6 +2327,7 @@ struct ImGuiContext
ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents
float NavWindowingTimer;
float NavWindowingHighlightAlpha;
ImGuiInputSource NavWindowingInputSource;
bool NavWindowingToggleLayer;
ImGuiKey NavWindowingToggleKey;
ImVec2 NavWindowingAccumDeltaPos;
@@ -2317,7 +2398,8 @@ struct ImGuiContext
// Widget state
ImGuiInputTextState InputTextState;
ImGuiInputTextDeactivatedState InputTextDeactivatedState;
ImFont InputTextPasswordFont;
ImFontBaked InputTextPasswordFontBackupBaked;
ImFontFlags InputTextPasswordFontBackupFlags;
ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types
int BeginMenuDepth;
@@ -2349,9 +2431,13 @@ struct ImGuiContext
ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest()
// Platform support
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame. Will be applied at end of the frame. For some backends, this is required to have WantVisible=true in order to receive text message.
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler.
// Extensions
// FIXME: We could provide an API to register one slot in an array held in ImGuiContext?
ImVector<ImTextureData*> UserTextures; // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[].
// Settings
bool SettingsLoaded;
float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero
@@ -2409,6 +2495,10 @@ struct ImGuiContext
ImGuiMetricsConfig DebugMetricsConfig;
ImGuiIDStackTool DebugIDStackTool;
ImGuiDebugAllocInfo DebugAllocInfo;
#if defined(IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS)
ImGuiStorage DebugDrawIdConflictsAliveCount;
ImGuiStorage DebugDrawIdConflictsHighlightSet;
#endif
// Misc
float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames..
@@ -2417,7 +2507,7 @@ struct ImGuiContext
float FramerateSecPerFrameAccum;
int WantCaptureMouseNextFrame; // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.
int WantCaptureKeyboardNextFrame; // "
int WantTextInputNextFrame;
int WantTextInputNextFrame; // Copied in EndFrame() from g.PlatformImeData.WantTextInput. Needs to be set for some backends (SDL3) to emit character inputs.
ImVector<char> TempBuffer; // Temporary text buffer
char TempKeychordName[64];
@@ -2463,7 +2553,8 @@ struct IMGUI_API ImGuiWindowTempData
ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement
int TreeDepth; // Current tree depth.
ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
ImU32 TreeHasStackDataDepthMask; // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
ImU32 TreeRecordsClippedNodesY2Mask; // Store whether we should keep recording Y2. Cleared when passing clip max. Equivalent TreeHasStackDataDepthMask value should always be set.
ImVector<ImGuiWindow*> ChildWindows;
ImGuiStorage* StateStorage; // Current persistent per-window storage (store e.g. tree node open/close state)
ImGuiOldColumns* CurrentColumns; // Current columns set
@@ -2606,9 +2697,11 @@ public:
// We don't use g.FontSize because the window may be != g.CurrentWindow.
ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontBaseSize * FontWindowScale * FontWindowScaleParents; }
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); }
ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); }
// [Obsolete] ImGuiWindow::CalcFontSize() was removed in 1.92.x because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window.
//float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontWindowScaleParents;
};
//-----------------------------------------------------------------------------
@@ -2698,11 +2791,7 @@ struct IMGUI_API ImGuiTabBar
//-----------------------------------------------------------------------------
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
#define IMGUI_TABLE_MAX_COLUMNS 512 // May be further lifted
// Our current column maximum is 64 but we may raise that in the future.
typedef ImS16 ImGuiTableColumnIdx;
typedef ImU16 ImGuiTableDrawChannelIdx;
#define IMGUI_TABLE_MAX_COLUMNS 512 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
// [Internal] sizeof() ~ 112
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
@@ -2901,7 +2990,7 @@ struct IMGUI_API ImGuiTable
bool IsSortSpecsDirty;
bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag.
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
bool DisableDefaultContextMenu; // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup()
bool DisableDefaultContextMenu; // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup()
bool IsSettingsRequestLoad;
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
@@ -3035,9 +3124,18 @@ namespace ImGui
IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
// Fonts, drawing
IMGUI_API void SetCurrentFont(ImFont* font);
inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture
IMGUI_API void UnregisterUserTexture(ImTextureData* tex);
IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas);
IMGUI_API void SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling);
IMGUI_API void UpdateCurrentFontSize(float restore_font_size_after_scaling);
IMGUI_API void SetFontRasterizerDensity(float rasterizer_density);
inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }
inline float GetRoundedFontSize(float size) { return IM_ROUND(size); }
IMGUI_API ImFont* GetDefaultFont();
IMGUI_API void PushPasswordFont();
IMGUI_API void PopPasswordFont();
inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
@@ -3049,7 +3147,7 @@ namespace ImGui
// NewFrame
IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs);
IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
IMGUI_API void UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos);
IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window);
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
IMGUI_API void UpdateMouseMovingWindowNewFrame();
@@ -3175,7 +3273,7 @@ namespace ImGui
IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data);
IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data);
IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
@@ -3191,7 +3289,7 @@ namespace ImGui
// This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are
// much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones.
IMGUI_API void FocusItem(); // Focus last item (no selection/activation).
IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again.
IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. Was called 'ActivateItem()' before 1.89.7.
// Inputs
// FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
@@ -3338,6 +3436,8 @@ namespace ImGui
IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel();
IMGUI_API void TablePushColumnChannel(int column_n);
IMGUI_API void TablePopColumnChannel();
IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);
// Tables: Internals
@@ -3414,7 +3514,7 @@ namespace ImGui
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
@@ -3433,11 +3533,15 @@ namespace ImGui
IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
// Widgets
// Widgets: Text
IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
IMGUI_API void TextAligned(float align_x, float size_x, const char* fmt, ...); // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
IMGUI_API void TextAlignedV(float align_x, float size_x, const char* fmt, va_list args);
// Widgets
IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);
IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);
@@ -3461,6 +3565,8 @@ namespace ImGui
// Widgets: Tree Nodes
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
IMGUI_API void TreeNodeDrawLineToChildNode(const ImVec2& target_pos);
IMGUI_API void TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);
IMGUI_API void TreePushOverrideID(ImGuiID id);
IMGUI_API bool TreeNodeGetOpen(ImGuiID storage_id);
IMGUI_API void TreeNodeSetOpen(ImGuiID storage_id, bool open);
@@ -3541,7 +3647,9 @@ namespace ImGui
IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);
IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
IMGUI_API void DebugNodeFont(ImFont* font);
IMGUI_API void DebugNodeFontGlyphesForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask);
IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);
IMGUI_API void DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect = NULL); // ID used to facilitate persisting the "current" texture.
IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label);
IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
IMGUI_API void DebugNodeTable(ImGuiTable* table);
@@ -3563,6 +3671,7 @@ namespace ImGui
//inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89
//inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
// Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
// Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
// (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)'
// (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0'
@@ -3574,31 +3683,192 @@ namespace ImGui
} // namespace ImGui
//-----------------------------------------------------------------------------
// [SECTION] ImFontLoader
//-----------------------------------------------------------------------------
// Hooks and storage for a given font backend.
// This structure is likely to evolve as we add support for incremental atlas updates.
// Conceptually this could be public, but API is still going to be evolve.
struct ImFontLoader
{
const char* Name;
bool (*LoaderInit)(ImFontAtlas* atlas);
void (*LoaderShutdown)(ImFontAtlas* atlas);
bool (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src);
void (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src);
bool (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);
bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x);
// Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.
// FIXME: At this point the two other types of buffers may be managed by core to be consistent?
size_t FontBakedSrcLoaderDataSize;
ImFontLoader() { memset(this, 0, sizeof(*this)); }
};
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype();
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are not actually compatible but we provide this as a compile-time error report helper.
#endif
//-----------------------------------------------------------------------------
// [SECTION] ImFontAtlas internal API
//-----------------------------------------------------------------------------
// This structure is likely to evolve as we add support for incremental atlas updates.
// Conceptually this could be in ImGuiPlatformIO, but we are far from ready to make this public.
struct ImFontBuilderIO
#define IMGUI_FONT_SIZE_MAX (512.0f)
#define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE (128.0f)
// Helpers: ImTextureRef ==/!= operators provided as convenience
// (note that _TexID and _TexData are never set simultaneously)
inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; }
inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs) { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; }
// Refer to ImFontAtlasPackGetRect() to better understand how this works.
#define ImFontAtlasRectId_IndexMask_ (0x000FFFFF) // 20-bits: index to access builder->RectsIndex[].
#define ImFontAtlasRectId_GenerationMask_ (0x3FF00000) // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.
#define ImFontAtlasRectId_GenerationShift_ (20)
inline int ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id) { return id & ImFontAtlasRectId_IndexMask_; }
inline int ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id) { return (id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx) { IM_ASSERT(index_idx < ImFontAtlasRectId_IndexMask_ && gen_idx < (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)
// User are returned ImFontAtlasRectId values which are meant to be persistent.
// We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId.
// RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code.
// Having this also makes it easier to e.g. sort rectangles during repack.
struct ImFontAtlasRectEntry
{
bool (*FontBuilder_Build)(ImFontAtlas* atlas);
int TargetIndex : 20; // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list.
int Generation : 10; // Increased each time the entry is reused for a new rectangle.
unsigned int IsUsed : 1;
};
// Helper for font builder
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype();
// Data available to potential texture post-processing functions
struct ImFontAtlasPostProcessData
{
ImFontAtlas* FontAtlas;
ImFont* Font;
ImFontConfig* FontSrc;
ImFontBaked* FontBaked;
ImFontGlyph* Glyph;
// Pixel data
void* Pixels;
ImTextureFormat Format;
int Pitch;
int Width;
int Height;
};
// We avoid dragging imstb_rectpack.h into public header (partly because binding generators are having issues with it)
#ifdef IMGUI_STB_NAMESPACE
namespace IMGUI_STB_NAMESPACE { struct stbrp_node; }
typedef IMGUI_STB_NAMESPACE::stbrp_node stbrp_node_im;
#else
struct stbrp_node;
typedef stbrp_node stbrp_node_im;
#endif
struct stbrp_context_opaque { char data[80]; };
// Internal storage for incrementally packing and building a ImFontAtlas
struct ImFontAtlasBuilder
{
stbrp_context_opaque PackContext; // Actually 'stbrp_context' but we don't want to define this in the header file.
ImVector<stbrp_node_im> PackNodes;
ImVector<ImTextureRect> Rects;
ImVector<ImFontAtlasRectEntry> RectsIndex; // ImFontAtlasRectId -> index into Rects[]
ImVector<unsigned char> TempBuffer; // Misc scratch buffer
int RectsIndexFreeListStart;// First unused entry
int RectsPackedCount; // Number of packed rectangles.
int RectsPackedSurface; // Number of packed pixels. Used when compacting to heuristically find the ideal texture size.
int RectsDiscardedCount;
int RectsDiscardedSurface;
int FrameCount; // Current frame count
ImVec2i MaxRectSize; // Largest rectangle to pack (de-facto used as a "minimum texture size")
ImVec2i MaxRectBounds; // Bottom-right most used pixels
bool LockDisableResize; // Disable resizing texture
bool PreloadedAllGlyphsRanges; // Set when missing ImGuiBackendFlags_RendererHasTextures features forces atlas to preload everything.
// Cache of all ImFontBaked
ImStableVector<ImFontBaked,32> BakedPool;
ImGuiStorage BakedMap; // BakedId --> ImFontBaked*
int BakedDiscardedCount;
// Custom rectangle identifiers
ImFontAtlasRectId PackIdMouseCursors; // White pixel + mouse cursors. Also happen to be fallback in case of packing failure.
ImFontAtlasRectId PackIdLinesTexData;
ImFontAtlasBuilder() { memset(this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; }
};
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildDestroy(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildMain(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader);
IMGUI_API void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char);
IMGUI_API void ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects
IMGUI_API ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h);
IMGUI_API void ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h);
IMGUI_API void ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_w = -1, int old_h = -1);
IMGUI_API void ImFontAtlasTextureCompact(ImFontAtlas* atlas);
IMGUI_API ImVec2i ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
IMGUI_API void ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy
IMGUI_API void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v);
IMGUI_API void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames);
IMGUI_API bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src);
IMGUI_API void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
IMGUI_API void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src);
IMGUI_API bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed
IMGUI_API void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font);
IMGUI_API void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames);
IMGUI_API ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
IMGUI_API ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id);
IMGUI_API void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked);
IMGUI_API ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);
IMGUI_API void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x);
IMGUI_API void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);
IMGUI_API void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch);
IMGUI_API void ImFontAtlasPackInit(ImFontAtlas* atlas);
IMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry = NULL);
IMGUI_API ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
IMGUI_API void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures);
IMGUI_API void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
IMGUI_API void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
IMGUI_API void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex);
IMGUI_API void ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data);
IMGUI_API void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor);
IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col);
IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);
IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);
IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status);
IMGUI_API const char* ImTextureDataGetFormatName(ImTextureFormat format);
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
IMGUI_API void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas);
#endif
IMGUI_API void ImFontAtlasUpdateSourcesPointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);
IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value);
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
IMGUI_API void ImFontAtlasBuildGetOversampleFactors(const ImFontConfig* src, int* out_oversample_h, int* out_oversample_v);
IMGUI_API bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]);

View File

@@ -141,6 +141,7 @@
// with previous char)
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
// (return type is int, -1 means not valid to insert)
// (not supported if you want to use UTF-8, see below)
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
// as manually wordwrapping for end-of-line positioning
@@ -178,6 +179,13 @@
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
//
// To support UTF-8:
//
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
// Instead, call stb_textedit_text() directly for text contents.
//
// Keyboard input must be encoded as a single integer value; e.g. a character code
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
@@ -250,8 +258,10 @@
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: [DEAR IMGUI] added 2024-09
// call this to text inputs sent to the textfield.
// text: (added 2025)
// call this to directly send text input the textfield, which is required
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// cannot infer text length.
//
//
// When rendering, you can read the cursor position and selection state from
@@ -400,6 +410,16 @@ typedef struct
#define IMSTB_TEXTEDIT_memmove memmove
#endif
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
#endif
/////////////////////////////////////////////////////////////////////////////
//
@@ -648,17 +668,6 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
}
}
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
#endif
#ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
@@ -668,9 +677,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
--c;
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
while (c >= 0 && !is_word_boundary(str, c))
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
if( c < 0 )
c = 0;
@@ -684,9 +693,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
while( c < len && !is_word_boundary( str, c ) )
++c;
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
if( c > len )
c = len;
@@ -739,6 +748,7 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
#define STB_TEXTEDIT_KEYTYPE int
#endif
// API key: process text input
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
@@ -753,8 +763,7 @@ static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* sta
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
else {
} else {
stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
stb_text_makeundo_insert(state, state->cursor, text_len);
@@ -771,6 +780,7 @@ retry:
switch (key) {
default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
// This is not suitable for UTF-8 support.
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
@@ -918,8 +928,9 @@ retry:
state->cursor = start;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@@ -927,7 +938,8 @@ retry:
x += dx;
if (x > goal_x)
break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@@ -980,8 +992,9 @@ retry:
state->cursor = find.prev_first;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@@ -989,7 +1002,8 @@ retry:
x += dx;
if (x > goal_x)
break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@@ -1002,8 +1016,13 @@ retry:
// go to previous line
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
--prev_scan;
while (prev_scan > 0)
{
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
prev_scan = prev;
}
find.first_char = find.prev_first;
find.prev_first = prev_scan;
}
@@ -1082,7 +1101,7 @@ retry:
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
@@ -1094,9 +1113,9 @@ retry:
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = n;
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
}
@@ -1110,7 +1129,7 @@ retry:
if (state->single_line)
state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;
@@ -1125,7 +1144,7 @@ retry:
if (state->single_line)
state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->select_end = state->cursor;
state->has_preferred_x = 0;
break;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.91b
// dear imgui, v1.92.1
// (tables and columns code)
/*
@@ -451,6 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table
}
// Push a standardized ID for both child-using and not-child-using tables
@@ -1250,7 +1251,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 11] Default context menu
// - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
// - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().
// - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
// - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
@@ -1510,6 +1511,7 @@ void ImGui::EndTable()
}
else
{
table->InnerWindow->DC.TreeDepth--;
ItemSize(table->OuterRect.GetSize());
ItemAdd(table->OuterRect, 0);
}
@@ -1951,7 +1953,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
IM_ASSERT(table->IsInsideRow);
if (table->CurrentColumn != -1)
{
TableEndCell(table);
table->CurrentColumn = -1;
}
// Logging
if (g.LogEnabled)
@@ -2191,6 +2196,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
g.LastItemData.StatusFlags = 0;
}
// Also see TablePushColumnChannel()
if (table->Flags & ImGuiTableFlags_NoClip)
{
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
@@ -2464,10 +2470,38 @@ void ImGui::TablePopBackgroundChannel()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable;
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
// Optimization: avoid PopClipRect() + SetCurrentChannel()
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
}
// Also see TableBeginCell()
void ImGui::TablePushColumnChannel(int column_n)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid SetCurrentChannel() + PushClipRect()
if (table->Flags & ImGuiTableFlags_NoClip)
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[column_n];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
void ImGui::TablePopColumnChannel()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid PopClipRect() + SetCurrentChannel()
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
@@ -3244,7 +3278,7 @@ void ImGui::TableHeader(const char* label)
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
// be merged into a single draw call.
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
if (text_clipped && hovered && g.ActiveId == 0)
@@ -3341,7 +3375,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id);
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
@@ -3396,7 +3430,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
PushStyleColor(ImGuiCol_Text, request->TextColor);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
PopStyleColor();
int vtx_idx_end = draw_list->_VtxCurrentIdx;

View File

@@ -1,4 +1,4 @@
// dear imgui, v1.91b
// dear imgui, v1.92.1
// (widgets code)
/*
@@ -339,6 +339,46 @@ void ImGui::TextWrappedV(const char* fmt, va_list args)
PopTextWrapPos();
}
void ImGui::TextAligned(float align_x, float size_x, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
TextAlignedV(align_x, size_x, fmt, args);
va_end(args);
}
// align_x: 0.0f = left, 0.5f = center, 1.0f = right.
// size_x : 0.0f = shortcut for GetContentRegionAvail().x
// FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
void ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list args)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
const char* text, *text_end;
ImFormatStringToTempBufferV(&text, &text_end, fmt, args);
const ImVec2 text_size = CalcTextSize(text, text_end);
size_x = CalcItemSize(ImVec2(size_x, 0.0f), 0.0f, text_size.y).x;
ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
ImVec2 pos_max(pos.x + size_x, window->ClipRect.Max.y);
ImVec2 size(ImMin(size_x, text_size.x), text_size.y);
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, pos.x + text_size.x);
window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, pos.x + text_size.x);
if (align_x > 0.0f && text_size.x < size_x)
pos.x += ImTrunc((size_x - text_size.x) * align_x);
RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size);
const ImVec2 backup_max_pos = window->DC.CursorMaxPos;
ItemSize(size);
ItemAdd(ImRect(pos, pos + size), 0);
window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content size because right-aligned text would otherwise mess it up.
if (size_x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip))
SetTooltip("%.*s", (int)(text_end - text), text);
}
void ImGui::LabelText(const char* label, const char* fmt, ...)
{
va_list args;
@@ -494,7 +534,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature.
// One idiom which was previously valid which will now emit a warning is when using multiple overlayed ButtonBehavior()
// One idiom which was previously valid which will now emit a warning is when using multiple overlaid ButtonBehavior()
// with same ID and different MouseButton (see #8030). You can fix it by:
// (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags.
// or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
@@ -508,6 +548,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);
if (flags & ImGuiButtonFlags_AllowOverlap)
item_flags |= ImGuiItemFlags_AllowOverlap;
if (item_flags & ImGuiItemFlags_NoFocus)
flags |= ImGuiButtonFlags_NoFocus | ImGuiButtonFlags_NoNavFocus;
// Default only reacts to left mouse button
if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)
@@ -518,7 +560,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_;
ImGuiWindow* backup_hovered_window = g.HoveredWindow;
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window->RootWindow;
if (flatten_hovered_children)
g.HoveredWindow = window;
@@ -531,7 +573,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
bool pressed = false;
bool hovered = ItemHoverable(bb, id, item_flags);
// Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
// Special mode for Drag and Drop used by openables (tree nodes, tabs etc.)
// where holding the button pressed for a long time while drag a payload item triggers the button.
if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
{
@@ -583,7 +626,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
SetFocusID(id, window);
FocusWindow(window);
}
else
else if (!(flags & ImGuiButtonFlags_NoFocus))
{
FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
}
@@ -601,7 +644,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
SetFocusID(id, window);
FocusWindow(window);
}
else
else if (!(flags & ImGuiButtonFlags_NoFocus))
{
FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
}
@@ -867,11 +910,12 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
if (hovered)
window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
ImU32 cross_col = GetColorU32(ImGuiCol_Text);
ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f);
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f);
const ImU32 cross_col = GetColorU32(ImGuiCol_Text);
const ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
const float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
const float cross_thickness = 1.0f; // FIXME-DPI
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, cross_thickness);
window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, cross_thickness);
return pressed;
}
@@ -1062,9 +1106,9 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
return held;
}
// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
// - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
void ImGui::ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@@ -1082,28 +1126,28 @@ void ImGui::ImageWithBg(ImTextureID user_texture_id, const ImVec2& image_size, c
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, ImDrawFlags_None, g.Style.ImageBorderSize);
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
}
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
{
ImageWithBg(user_texture_id, image_size, uv0, uv1);
ImageWithBg(tex_ref, image_size, uv0, uv1);
}
// 1.91.9 (February 2025) removed 'tint_col' and 'border_col' parameters, made border size not depend on color value. (#8131, #8238)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiContext& g = *GImGui;
PushStyleVar(ImGuiStyleVar_ImageBorderSize, (border_col.w > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); // Preserve legacy behavior where border is always visible when border_col's Alpha is >0.0f
PushStyleColor(ImGuiCol_Border, border_col);
ImageWithBg(user_texture_id, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
ImageWithBg(tex_ref, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
PopStyleColor();
PopStyleVar();
}
#endif
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@@ -1125,21 +1169,21 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID user_texture_id, const ImVec2&
RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
if (bg_col.w > 0.0f)
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
return pressed;
}
// - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) // FIXME: Maybe that's not the best design?
bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
bool ImGui::ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col);
return ImageButtonEx(window->GetID(str_id), tex_ref, image_size, uv0, uv1, bg_col, tint_col);
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -1477,8 +1521,8 @@ bool ImGui::TextLink(const char* label)
ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z);
}
float line_y = bb.Max.y + ImFloor(g.Font->Descent * g.FontScale * 0.20f);
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode.
float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f);
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode // FIXME-DPI
PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));
RenderText(bb.Min, label, label_end);
@@ -1488,14 +1532,14 @@ bool ImGui::TextLink(const char* label)
return pressed;
}
void ImGui::TextLinkOpenURL(const char* label, const char* url)
bool ImGui::TextLinkOpenURL(const char* label, const char* url)
{
ImGuiContext& g = *GImGui;
if (url == NULL)
url = label;
if (TextLink(label))
if (g.PlatformIO.Platform_OpenInShellFn != NULL)
g.PlatformIO.Platform_OpenInShellFn(&g, url);
bool pressed = TextLink(label);
if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)
g.PlatformIO.Platform_OpenInShellFn(&g, url);
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label
if (BeginPopupContextItem())
{
@@ -1503,6 +1547,7 @@ void ImGui::TextLinkOpenURL(const char* label, const char* url)
SetClipboardText(url);
EndPopup();
}
return pressed;
}
//-------------------------------------------------------------------------
@@ -1689,7 +1734,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
if (g.LogEnabled)
LogSetNextTextDecoration("---", NULL);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size);
}
else
{
@@ -3890,7 +3935,7 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si
return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
}
// This is only used in the path where the multiline widget is inactivate.
// This is only used in the path where the multiline widget is inactive.
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)
{
int line_count = 0;
@@ -3914,9 +3959,10 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char**
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end, const char** remaining, ImVec2* out_offset, bool stop_on_new_line)
{
ImGuiContext& g = *ctx;
ImFont* font = g.Font;
//ImFont* font = g.Font;
ImFontBaked* baked = g.FontBaked;
const float line_height = g.FontSize;
const float scale = line_height / font->FontSize;
const float scale = line_height / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
@@ -3942,8 +3988,7 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c
if (c == '\r')
continue;
const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale;
line_width += char_width;
line_width += baked->GetCharAdvance((ImWchar)c) * scale;
}
if (text_size.x < line_width)
@@ -3970,7 +4015,7 @@ namespace ImStb
{
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->TextLen; }
static char STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->TextLen); return obj->TextSrc[idx]; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance((ImWchar)c) * g.FontScale; }
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; }
static char STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
{
@@ -4238,23 +4283,24 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
if (new_text == new_text_end)
return;
ImGuiContext& g = *Ctx;
ImGuiInputTextState* obj = &g.InputTextState;
IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID);
// Grow internal buffer if needed
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text);
if (new_text_len + BufTextLen >= BufSize)
if (new_text_len + BufTextLen + 1 > obj->TextA.Size && (Flags & ImGuiInputTextFlags_ReadOnly) == 0)
{
if (!is_resizable)
return;
ImGuiContext& g = *Ctx;
ImGuiInputTextState* edit_state = &g.InputTextState;
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
IM_ASSERT(Buf == edit_state->TextA.Data);
IM_ASSERT(Buf == obj->TextA.Data);
int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
edit_state->TextA.resize(new_buf_size + 1);
edit_state->TextSrc = edit_state->TextA.Data;
Buf = edit_state->TextA.Data;
BufSize = edit_state->BufCapacity = new_buf_size;
obj->TextA.resize(new_buf_size + 1);
obj->TextSrc = obj->TextA.Data;
Buf = obj->TextA.Data;
BufSize = obj->BufCapacity = new_buf_size;
}
if (BufTextLen != pos)
@@ -4272,18 +4318,29 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
void ImGui::PushPasswordFont()
{
ImGuiContext& g = *GImGui;
ImFont* in_font = g.Font;
ImFont* out_font = &g.InputTextPasswordFont;
ImFontGlyph* glyph = in_font->FindGlyph('*');
out_font->FontSize = in_font->FontSize;
out_font->Scale = in_font->Scale;
out_font->Ascent = in_font->Ascent;
out_font->Descent = in_font->Descent;
out_font->ContainerAtlas = in_font->ContainerAtlas;
out_font->FallbackGlyph = glyph;
out_font->FallbackAdvanceX = glyph->AdvanceX;
IM_ASSERT(out_font->Glyphs.Size == 0 && out_font->IndexAdvanceX.Size == 0 && out_font->IndexLookup.Size == 0);
PushFont(out_font);
ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
ImFontGlyph* glyph = g.FontBaked->FindGlyph('*');
g.InputTextPasswordFontBackupFlags = g.Font->Flags;
backup->FallbackGlyphIndex = g.FontBaked->FallbackGlyphIndex;
backup->FallbackAdvanceX = g.FontBaked->FallbackAdvanceX;
backup->IndexLookup.swap(g.FontBaked->IndexLookup);
backup->IndexAdvanceX.swap(g.FontBaked->IndexAdvanceX);
g.Font->Flags |= ImFontFlags_NoLoadGlyphs;
g.FontBaked->FallbackGlyphIndex = g.FontBaked->Glyphs.index_from_ptr(glyph);
g.FontBaked->FallbackAdvanceX = glyph->AdvanceX;
}
void ImGui::PopPasswordFont()
{
ImGuiContext& g = *GImGui;
ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
g.Font->Flags = g.InputTextPasswordFontBackupFlags;
g.FontBaked->FallbackGlyphIndex = backup->FallbackGlyphIndex;
g.FontBaked->FallbackAdvanceX = backup->FallbackAdvanceX;
g.FontBaked->IndexLookup.swap(backup->IndexLookup);
g.FontBaked->IndexAdvanceX.swap(backup->IndexAdvanceX);
IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
}
// Return false to discard a character.
@@ -4647,7 +4704,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (g.ActiveId == id)
{
// Declare some inputs, the other are registered and polled via Shortcut() routing system.
// FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combinaison into individual shortcuts.
// FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts.
const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End };
for (ImGuiKey key : always_owned_keys)
SetKeyOwner(key, id);
@@ -5160,8 +5217,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Otherwise request text input ahead for next frame.
if (g.ActiveId == id && clear_active_id)
ClearActiveID();
else if (g.ActiveId == id)
g.WantTextInputNextFrame = 1;
// Render frame
if (!is_multiline)
@@ -5187,7 +5242,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (new_is_displaying_hint != is_displaying_hint)
{
if (is_password && !is_displaying_hint)
PopFont();
PopPasswordFont();
is_displaying_hint = new_is_displaying_hint;
if (is_password && !is_displaying_hint)
PushPasswordFont();
@@ -5312,7 +5367,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
else
{
ImVec2 rect_size = InputTextCalcTextSize(&g, p, text_selected_end, &p, NULL, true);
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
@@ -5339,14 +5394,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
if (!is_readonly)
// This is required for some backends (SDL3) to start emitting character/text inputs.
// As per #6341, make sure we don't set that on the deactivating frame.
if (!is_readonly && g.ActiveId == id)
{
g.PlatformImeData.WantVisible = true;
g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
g.PlatformImeData.InputLineHeight = g.FontSize;
ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
ime_data->WantVisible = true;
ime_data->WantTextInput = true;
ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
ime_data->InputLineHeight = g.FontSize;
ime_data->ViewportId = window->Viewport->ID;
}
}
}
@@ -5373,7 +5433,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
if (is_password && !is_displaying_hint)
PopFont();
PopPasswordFont();
if (is_multiline)
{
@@ -5425,7 +5485,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
DebugLocateItemOnHover(state->ID);
Text("CurLenA: %d, Cursor: %d, Selection: %d..%d", state->TextLen, stb_state->cursor, stb_state->select_start, stb_state->select_end);
Text("BufCapacityA: %d", state->BufCapacity);
Text("BufCapacity: %d", state->BufCapacity);
Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
@@ -6207,7 +6267,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
if (g.Style.FrameBorderSize > 0.0f)
RenderFrameBorder(bb.Min, bb.Max, rounding);
else
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border // FIXME-DPI
}
// Drag and Drop Source
@@ -6389,6 +6449,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
// - TreeNodeV()
// - TreeNodeEx()
// - TreeNodeExV()
// - TreeNodeStoreStackData() [Internal]
// - TreeNodeBehavior() [Internal]
// - TreePush()
// - TreePop()
@@ -6547,18 +6608,26 @@ bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags)
// Store ImGuiTreeNodeStackData for just submitted node.
// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase.
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags)
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1);
ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.back();
ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
tree_node_data->ID = g.LastItemData.ID;
tree_node_data->TreeFlags = flags;
tree_node_data->ItemFlags = g.LastItemData.ItemFlags;
tree_node_data->NavRect = g.LastItemData.NavRect;
// Initially I tried to latch value for GetColorU32(ImGuiCol_TreeLines) but it's not a good trade-off for very large trees.
const bool draw_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) != 0;
tree_node_data->DrawLinesX1 = draw_lines ? (x1 + g.FontSize * 0.5f + g.Style.FramePadding.x) : +FLT_MAX;
tree_node_data->DrawLinesTableColumn = (draw_lines && g.CurrentTable) ? (ImGuiTableColumnIdx)g.CurrentTable->CurrentColumn : -1;
tree_node_data->DrawLinesToNodesY2 = -FLT_MAX;
window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);
if (flags & ImGuiTreeNodeFlags_DrawLinesToNodes)
window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth);
}
// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop.
@@ -6628,14 +6697,18 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
g.LastItemData.DisplayRect = frame_bb;
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled:
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsToParent enabled:
// Store data for the current depth to allow returning to this node from any child item.
// For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
// It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle.
// It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsToParent by default or move it to ImGuiStyle.
bool store_tree_node_stack_data = false;
if ((flags & ImGuiTreeNodeFlags_DrawLinesMask_) == 0)
flags |= g.Style.TreeLinesFlags;
const bool draw_tree_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) && (frame_bb.Min.y < window->ClipRect.Max.y) && (g.Style.TreeLinesSize > 0.0f);
if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
{
if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive)
store_tree_node_stack_data = draw_tree_lines;
if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsToParent) && !g.NavIdIsAlive)
if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
store_tree_node_stack_data = true;
}
@@ -6643,8 +6716,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
if (!is_visible)
{
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
if ((flags & ImGuiTreeNodeFlags_DrawLinesToNodes) && (window->DC.TreeRecordsClippedNodesY2Mask & (1 << (window->DC.TreeDepth - 1))))
{
ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, window->DC.CursorPos.y); // Don't need to aim to mid Y position as we are clipped anyway.
if (frame_bb.Min.y >= window->ClipRect.Max.y)
window->DC.TreeRecordsClippedNodesY2Mask &= ~(1 << (window->DC.TreeDepth - 1)); // Done
}
if (is_open && store_tree_node_stack_data)
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushOverrideID(id);
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
@@ -6690,6 +6770,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
else
button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
if (flags & ImGuiTreeNodeFlags_NoNavFocus)
button_flags |= ImGuiButtonFlags_NoNavFocus;
bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
const bool was_selected = selected;
@@ -6776,6 +6858,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
@@ -6796,6 +6880,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
}
RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
@@ -6804,8 +6890,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
LogSetNextTextDecoration(">", NULL);
}
if (span_all_columns && !span_all_columns_label)
TablePopBackgroundChannel();
if (draw_tree_lines)
TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.5f));
// Label
if (display_frame)
@@ -6817,8 +6903,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
TablePopBackgroundChannel();
}
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
if (is_open && store_tree_node_stack_data)
TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice
@@ -6826,6 +6912,64 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
return is_open;
}
// Draw horizontal line from our parent node
// This is only called for visible child nodes so we are not too fussy anymore about performances
void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->DC.TreeDepth == 0 || (window->DC.TreeHasStackDataDepthMask & (1 << (window->DC.TreeDepth - 1))) == 0)
return;
ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
float x1 = ImTrunc(parent_data->DrawLinesX1);
float x2 = ImTrunc(target_pos.x - g.Style.ItemInnerSpacing.x);
float y = ImTrunc(target_pos.y);
float rounding = (g.Style.TreeLinesRounding > 0.0f) ? ImMin(x2 - x1, g.Style.TreeLinesRounding) : 0.0f;
parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, y - rounding);
if (x1 >= x2)
return;
if (rounding > 0.0f)
{
x1 += 0.5f + rounding;
window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3);
if (x1 < x2)
window->DrawList->PathLineTo(ImVec2(x2, y));
window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize);
}
else
{
window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
}
}
// Draw vertical line of the hierarchy
void ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
float y1 = ImMax(data->NavRect.Max.y, window->ClipRect.Min.y);
float y2 = data->DrawLinesToNodesY2;
if (data->TreeFlags & ImGuiTreeNodeFlags_DrawLinesFull)
{
float y2_full = window->DC.CursorPos.y;
if (g.CurrentTable)
y2_full = ImMax(g.CurrentTable->RowPosY2, y2_full);
y2_full = ImTrunc(y2_full - g.Style.ItemSpacing.y - g.FontSize * 0.5f);
if (y2 + (g.Style.ItemSpacing.y + g.Style.TreeLinesRounding) < y2_full) // FIXME: threshold to use ToNodes Y2 instead of Full Y2 when close by ItemSpacing.y
y2 = y2_full;
}
y2 = ImMin(y2, window->ClipRect.Max.y);
if (y2 <= y1)
return;
float x = ImTrunc(data->DrawLinesX1);
if (data->DrawLinesTableColumn != -1)
TablePushColumnChannel(data->DrawLinesTableColumn);
window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
if (data->DrawLinesTableColumn != -1)
TablePopColumnChannel();
}
void ImGui::TreePush(const char* str_id)
{
ImGuiWindow* window = GetCurrentWindow();
@@ -6860,18 +7004,23 @@ void ImGui::TreePop()
window->DC.TreeDepth--;
ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);
if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask) // Only set during request
if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask)
{
ImGuiTreeNodeStackData* data = &g.TreeNodeStack.back();
const ImGuiTreeNodeStackData* data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
IM_ASSERT(data->ID == window->IDStack.back());
if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere)
{
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled)
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsToParent is enabled)
if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsToParent)
if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data);
}
// Draw hierarchy lines
if (data->DrawLinesX1 != +FLT_MAX && window->DC.CursorPos.y >= window->ClipRect.Min.y)
TreeNodeDrawLineToTreePop(data);
g.TreeNodeStack.pop_back();
window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask;
window->DC.TreeRecordsClippedNodesY2Mask &= ~tree_depth_mask;
}
IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
@@ -7486,7 +7635,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
ImRect box_select_r = bs->BoxSelectRectCurr;
box_select_r.ClipWith(scope_rect);
window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT: Styling
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling
// Scroll
const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;
@@ -7690,7 +7839,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
if (ms->IsFocused)
{
// We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here.
if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure)
if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at beginning of the scope (see tests for easy failure)
{
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId.
storage->RangeSrcItem = ImGuiSelectionUserData_Invalid;
@@ -10376,13 +10525,12 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
#endif
// Render text label (with clipping + alpha gradient) + unsaved marker
ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;
ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
// Return clipped state ignoring the close button
if (out_text_clipped)
{
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x;
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x;
//draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
}
@@ -10428,15 +10576,22 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
// This is all rather complicated
// (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)
// FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..
float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f;
float ellipsis_max_x = text_ellipsis_clip_bb.Max.x;
if (close_button_visible || unsaved_marker_visible)
{
text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f);
text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f;
ellipsis_max_x = text_pixel_clip_bb.Max.x;
const bool visible_without_hover = unsaved_marker_visible || (is_contents_visible ? g.Style.TabCloseButtonMinWidthSelected : g.Style.TabCloseButtonMinWidthUnselected) < 0.0f;
if (visible_without_hover)
{
text_ellipsis_clip_bb.Max.x -= button_sz * 0.90f;
ellipsis_max_x -= button_sz * 0.90f;
}
else
{
text_ellipsis_clip_bb.Max.x -= button_sz * 1.00f;
}
}
LogSetNextTextDecoration("/", "\\");
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size);
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size);
#if 0
if (!is_contents_visible)

View File

@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
q2[0] = (float)x2;
q2[1] = (float)y2;
if (equal(q0,q1) || equal(q1,q2)) {
x0 = (int)verts[i-1].x;
y0 = (int)verts[i-1].y;
x0 = (int)verts[i-1].x; //-V1048
y0 = (int)verts[i-1].y; //-V1048
x1 = (int)verts[i ].x;
y1 = (int)verts[i ].y;
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {

View File

@@ -196,6 +196,7 @@ add_library(zip STATIC
lib/zip_name_locate.c
lib/zip_new.c
lib/zip_open.c
lib/zip_realloc.c
lib/zip_pkware.c
lib/zip_progress.c
lib/zip_rename.c

View File

@@ -34,6 +34,15 @@
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(__has_feature)
#if !__has_feature(nullability)
#define _Nullable
#define _Nonnull
#endif
#else
#define _Nullable
#define _Nonnull
#endif
#ifdef __cplusplus
extern "C" {

View File

@@ -44,10 +44,7 @@ _zip_add_entry(zip_t *za) {
zip_uint64_t idx;
if (za->nentry + 1 >= za->nentry_alloc) {
zip_entry_t *rentries;
zip_uint64_t nalloc = za->nentry_alloc;
zip_uint64_t additional_entries = 2 * nalloc;
zip_uint64_t realloc_size;
zip_uint64_t additional_entries = 2 * za->nentry_alloc;
if (additional_entries < 16) {
additional_entries = 16;
@@ -55,21 +52,10 @@ _zip_add_entry(zip_t *za) {
else if (additional_entries > 1024) {
additional_entries = 1024;
}
/* neither + nor * overflows can happen: nentry_alloc * sizeof(struct zip_entry) < UINT64_MAX */
nalloc += additional_entries;
realloc_size = sizeof(struct zip_entry) * (size_t)nalloc;
if (sizeof(struct zip_entry) * (size_t)za->nentry_alloc > realloc_size) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
if (!ZIP_REALLOC(za->entry, za->nentry_alloc, additional_entries, &za->error)) {
return -1;
}
rentries = (zip_entry_t *)realloc(za->entry, sizeof(struct zip_entry) * (size_t)nalloc);
if (rentries == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return -1;
}
za->entry = rentries;
za->nentry_alloc = nalloc;
}
idx = za->nentry++;

View File

@@ -208,11 +208,11 @@ input(void *ud, zip_uint8_t *data, zip_uint64_t length) {
}
static void
end_of_input(void *ud) {
static bool end_of_input(void *ud) {
struct ctx *ctx = (struct ctx *)ud;
ctx->end_of_input = true;
return ctx->zstr.avail_in != 0;
}

View File

@@ -196,11 +196,11 @@ input(void *ud, zip_uint8_t *data, zip_uint64_t length) {
}
static void
end_of_input(void *ud) {
static bool end_of_input(void *ud) {
struct ctx *ctx = (struct ctx *)ud;
ctx->end_of_input = true;
return ctx->zstr.avail_in != 0;
}

View File

@@ -302,11 +302,11 @@ input(void *ud, zip_uint8_t *data, zip_uint64_t length) {
}
static void
end_of_input(void *ud) {
static bool end_of_input(void *ud) {
struct ctx *ctx = (struct ctx *)ud;
ctx->end_of_input = true;
return ctx->zstr.avail_in != 0;
}

View File

@@ -211,11 +211,11 @@ input(void *ud, zip_uint8_t *data, zip_uint64_t length) {
}
static void
end_of_input(void *ud) {
static bool end_of_input(void *ud) {
struct ctx *ctx = (struct ctx *)ud;
ctx->end_of_input = true;
return ctx->in.pos != ctx->in.size;
}

View File

@@ -42,7 +42,7 @@
#endif
static int add_data(zip_t *, zip_source_t *, zip_dirent_t *, zip_uint32_t);
static int add_data(zip_t *, zip_source_t *, zip_dirent_t *);
static int copy_data(zip_t *, zip_uint64_t);
static int copy_source(zip_t *, zip_source_t *, zip_source_t *, zip_int64_t);
static int torrentzip_compare_names(const void *a, const void *b);
@@ -195,6 +195,12 @@ zip_close(zip_t *za) {
break;
}
}
else if (entry->orig != NULL) {
if (!_zip_dirent_merge(entry->changes, entry->orig, ZIP_ENTRY_DATA_CHANGED(entry), &za->error)) {
error = 1;
break;
}
}
de = entry->changes;
if (_zip_read_local_ef(za, i) < 0) {
@@ -225,7 +231,7 @@ zip_close(zip_t *za) {
}
/* add_data writes dirent */
if (add_data(za, zs ? zs : entry->source, de, entry->changes ? entry->changes->changed : 0) < 0) {
if (add_data(za, zs ? zs : entry->source, de) < 0) {
error = 1;
if (zs)
zip_source_free(zs);
@@ -295,8 +301,7 @@ zip_close(zip_t *za) {
}
static int
add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) {
static int add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) {
zip_int64_t offstart, offdata, offend, data_length;
zip_stat_t st;
zip_file_attributes_t attributes;
@@ -305,19 +310,25 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) {
int is_zip64;
zip_flags_t flags;
bool needs_recompress, needs_decompress, needs_crc, needs_compress, needs_reencrypt, needs_decrypt, needs_encrypt;
bool dirent_changed;
bool have_dos_time = false;
time_t mtime_before_copy;
if (zip_source_stat(src, &st) < 0) {
zip_error_set_from_source(&za->error, src);
return -1;
}
de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) {
st.valid |= ZIP_STAT_COMP_METHOD;
st.comp_method = ZIP_CM_STORE;
}
if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE)
if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE) {
de->comp_method = st.comp_method;
}
else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) {
st.valid |= ZIP_STAT_COMP_SIZE;
st.comp_size = st.size;
@@ -372,14 +383,30 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) {
}
}
if ((offstart = zip_source_tell_write(za->src)) < 0) {
zip_error_set_from_source(&za->error, za->src);
return -1;
if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) {
int ret2 = zip_source_get_dos_time(src, &de->last_mod);
if (ret2 < 0) {
zip_error_set_from_source(&za->error, src);
return -1;
}
if (ret2 == 1) {
have_dos_time = true;
}
else {
if (st.valid & ZIP_STAT_MTIME) {
mtime_before_copy = st.mtime;
}
else {
time(&mtime_before_copy);
}
if (_zip_u2d_time(mtime_before_copy, &de->last_mod, &za->error) < 0) {
return -1;
}
}
}
/* as long as we don't support non-seekable output, clear data descriptor bit */
de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR;
if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) {
if ((offstart = zip_source_tell_write(za->src)) < 0) {
zip_error_set_from_source(&za->error, za->src);
return -1;
}
@@ -485,9 +512,24 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) {
src_final = src_tmp;
}
if (!ZIP_WANT_TORRENTZIP(za)) {
if (zip_source_get_file_attributes(src_final, &attributes) != 0) {
zip_error_set_from_source(&za->error, src_final);
zip_source_free(src_final);
return -1;
}
_zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0);
}
/* as long as we don't support non-seekable output, clear data descriptor bit */
if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) {
zip_source_free(src_final);
return -1;
}
if ((offdata = zip_source_tell_write(za->src)) < 0) {
zip_error_set_from_source(&za->error, za->src);
zip_source_free(src_final);
return -1;
}
@@ -498,9 +540,11 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) {
ret = -1;
}
if (zip_source_get_file_attributes(src_final, &attributes) != 0) {
zip_error_set_from_source(&za->error, src_final);
ret = -1;
if (!ZIP_WANT_TORRENTZIP(za)) {
if (zip_source_get_file_attributes(src_final, &attributes) != 0) {
zip_error_set_from_source(&za->error, src_final);
ret = -1;
}
}
zip_source_free(src_final);
@@ -514,57 +558,51 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) {
return -1;
}
if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) {
zip_error_set_from_source(&za->error, za->src);
return -1;
}
if ((st.valid & (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) {
zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
return -1;
}
if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) {
int ret2 = zip_source_get_dos_time(src, &de->last_mod);
if (ret2 < 0) {
zip_error_set_from_source(&za->error, src);
return -1;
}
if (ret2 == 0) {
time_t mtime;
if (st.valid & ZIP_STAT_MTIME) {
mtime = st.mtime;
}
else {
time(&mtime);
}
if (_zip_u2d_time(mtime, &de->last_mod, &za->error) < 0) {
return -1;
}
}
}
dirent_changed = ZIP_CM_ACTUAL(de->comp_method) != st.comp_method || de->crc != st.crc || de->uncomp_size != st.size || de->comp_size != (zip_uint64_t)(offend - offdata);
de->comp_method = st.comp_method;
de->crc = st.crc;
de->uncomp_size = st.size;
de->comp_size = (zip_uint64_t)(offend - offdata);
_zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed);
if (ZIP_WANT_TORRENTZIP(za)) {
zip_dirent_torrentzip_normalize(de);
if (!ZIP_WANT_TORRENTZIP(za)) {
dirent_changed |= _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0);
if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0 && !have_dos_time) {
if (st.valid & ZIP_STAT_MTIME) {
if (st.mtime != mtime_before_copy) {
if (_zip_u2d_time(st.mtime, &de->last_mod, &za->error) < 0) {
return -1;
}
dirent_changed = true;
}
}
}
}
if ((ret = _zip_dirent_write(za, de, flags)) < 0)
return -1;
if (dirent_changed) {
if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) {
zip_error_set_from_source(&za->error, za->src);
return -1;
}
if (is_zip64 != ret) {
/* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */
zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
return -1;
}
if ((ret = _zip_dirent_write(za, de, flags)) < 0)
return -1;
if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) {
zip_error_set_from_source(&za->error, za->src);
return -1;
if (is_zip64 != ret) {
/* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */
zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
return -1;
}
if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) {
zip_error_set_from_source(&za->error, za->src);
return -1;
}
}
if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {

View File

@@ -83,32 +83,21 @@ _zip_cdir_new(zip_error_t *error) {
bool
_zip_cdir_grow(zip_cdir_t *cd, zip_uint64_t additional_entries, zip_error_t *error) {
zip_uint64_t i, new_alloc;
zip_entry_t *new_entry;
zip_uint64_t i;
if (additional_entries == 0) {
return true;
}
new_alloc = cd->nentry_alloc + additional_entries;
if (new_alloc < additional_entries || new_alloc > SIZE_MAX / sizeof(*(cd->entry))) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
if (!ZIP_REALLOC(cd->entry, cd->nentry_alloc, additional_entries, error)) {
return false;
}
if ((new_entry = (zip_entry_t *)realloc(cd->entry, sizeof(*(cd->entry)) * (size_t)new_alloc)) == NULL) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
return false;
}
cd->entry = new_entry;
for (i = cd->nentry; i < new_alloc; i++) {
for (i = cd->nentry; i < cd->nentry_alloc; i++) {
_zip_entry_init(cd->entry + i);
}
cd->nentry = cd->nentry_alloc = new_alloc;
cd->nentry = cd->nentry_alloc;
return true;
}
@@ -275,6 +264,55 @@ _zip_dirent_free(zip_dirent_t *zde) {
}
bool
_zip_dirent_merge(zip_dirent_t *de, zip_dirent_t *de_orig, bool replacing_data, zip_error_t *error) {
if (!de->cloned) {
zip_error_set(error, ZIP_ER_INTERNAL, 0);
return false;
}
if (!(de->changed & ZIP_DIRENT_ATTRIBUTES)) {
de->ext_attrib = de_orig->ext_attrib;
de->int_attrib = de_orig->int_attrib;
}
if (!(de->changed & ZIP_DIRENT_COMMENT)) {
de->comment = de_orig->comment;
}
if (!(de->changed & ZIP_DIRENT_COMP_METHOD)) {
if (replacing_data) {
de->comp_method = ZIP_CM_DEFAULT;
de->compression_level = 0;
}
else {
de->comp_method = de_orig->comp_method;
de->compression_level = de_orig->compression_level;
}
}
if (!(de->changed & ZIP_DIRENT_ENCRYPTION_METHOD)) {
if (replacing_data) {
de->encryption_method = ZIP_EM_NONE;
}
else {
de->encryption_method = de_orig->encryption_method;
}
}
if (!(de->changed & ZIP_DIRENT_EXTRA_FIELD)) {
de->extra_fields = de_orig->extra_fields;
}
if (!(de->changed & ZIP_DIRENT_FILENAME)) {
de->filename = de_orig->filename;
}
if (!(de->changed & ZIP_DIRENT_LAST_MOD)) {
de->last_mod = de_orig->last_mod;
}
if (!(de->changed & ZIP_DIRENT_PASSWORD)) {
de->password = de_orig->password;
}
return true;
}
void
_zip_dirent_init(zip_dirent_t *de) {
de->changed = 0;
@@ -968,7 +1006,7 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) {
_zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES);
}
else {
_zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method);
_zip_buffer_put_16(buffer, (zip_uint16_t)ZIP_CM_ACTUAL(de->comp_method));
}
if (ZIP_WANT_TORRENTZIP(za)) {
@@ -1190,52 +1228,76 @@ _zip_u2d_time(time_t intime, zip_dostime_t *dtime, zip_error_t *ze) {
}
void
_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64, zip_uint32_t changed) {
bool
_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64) {
zip_uint16_t length;
bool has_changed = false;
if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) {
zip_uint16_t mask = attributes->general_purpose_bit_mask & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
de->bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask);
zip_uint16_t bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask);
if (de->bitflags != bitflags) {
de->bitflags = bitflags;
has_changed = true;
}
}
if (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) {
de->int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0);
zip_uint16_t int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0);
if (de->int_attrib != int_attrib) {
de->int_attrib = int_attrib;
has_changed = true;
}
}
/* manually set attributes are preferred over attributes provided by source */
if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) {
de->ext_attrib = attributes->external_file_attributes;
if ((de->changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) {
if (de->ext_attrib != attributes->external_file_attributes) {
de->ext_attrib = attributes->external_file_attributes;
has_changed = true;
}
}
zip_uint16_t version_needed;
if (de->comp_method == ZIP_CM_LZMA) {
de->version_needed = 63;
version_needed = 63;
}
else if (de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256) {
de->version_needed = 51;
version_needed = 51;
}
else if (de->comp_method == ZIP_CM_BZIP2) {
de->version_needed = 46;
version_needed = 46;
}
else if (force_zip64 || _zip_dirent_needs_zip64(de, 0)) {
de->version_needed = 45;
version_needed = 45;
}
else if (de->comp_method == ZIP_CM_DEFLATE || de->encryption_method == ZIP_EM_TRAD_PKWARE) {
de->version_needed = 20;
version_needed = 20;
}
else if ((length = _zip_string_length(de->filename)) > 0 && de->filename->raw[length - 1] == '/') {
de->version_needed = 20;
version_needed = 20;
}
else {
de->version_needed = 10;
version_needed = 10;
}
if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) {
de->version_needed = ZIP_MAX(de->version_needed, attributes->version_needed);
version_needed = ZIP_MAX(version_needed, attributes->version_needed);
}
de->version_madeby = 63 | (de->version_madeby & 0xff00);
if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) {
de->version_madeby = (de->version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8);
if (de->version_needed != version_needed) {
de->version_needed = version_needed;
has_changed = true;
}
zip_int16_t version_madeby = 63 | (de->version_madeby & 0xff00);
if ((de->changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) {
version_madeby = (version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8);
}
if (de->version_madeby != version_madeby) {
de->version_madeby = version_madeby;
has_changed = true;
}
return has_changed;
}
@@ -1259,13 +1321,36 @@ zip_dirent_torrentzip_normalize(zip_dirent_t *de) {
int
zip_dirent_check_consistency(zip_dirent_t *dirent) {
if (dirent->comp_method == ZIP_CM_STORE && dirent->comp_size != dirent->uncomp_size) {
return ZIP_ER_DETAIL_STORED_SIZE_MISMATCH;
if (dirent->comp_method == ZIP_CM_STORE) {
zip_uint64_t header_size = 0;
switch (dirent->encryption_method) {
case ZIP_EM_NONE:
break;
case ZIP_EM_TRAD_PKWARE:
header_size = 12;
break;
case ZIP_EM_AES_128:
header_size = 20;
break;
case ZIP_EM_AES_192:
header_size = 24;
break;
case ZIP_EM_AES_256:
header_size = 28;
break;
default:
return 0;
}
if (dirent->uncomp_size + header_size < dirent->uncomp_size || dirent->comp_size != dirent->uncomp_size + header_size) {
return ZIP_ER_DETAIL_STORED_SIZE_MISMATCH;
}
}
return 0;
}
time_t zip_dirent_get_last_mod_mtime(zip_dirent_t *de) {
time_t
zip_dirent_get_last_mod_mtime(zip_dirent_t *de) {
if (!de->last_mod_mtime_valid) {
de->last_mod_mtime = _zip_d2u_time(&de->last_mod);
de->last_mod_mtime_valid = true;

View File

@@ -41,6 +41,9 @@ ZIP_EXTERN int
zip_fclose(zip_file_t *zf) {
int ret;
if (zf == NULL)
return ZIP_ER_INVAL;
if (zf->src)
zip_source_free(zf->src);

View File

@@ -40,7 +40,7 @@
ZIP_EXTERN int
zip_file_set_encryption(zip_t *za, zip_uint64_t idx, zip_uint16_t method, const char *password) {
zip_entry_t *e;
zip_uint16_t old_method;
char *our_password = NULL;
if (idx >= za->nentry) {
zip_error_set(&za->error, ZIP_ER_INVAL, 0);
@@ -63,56 +63,33 @@ zip_file_set_encryption(zip_t *za, zip_uint64_t idx, zip_uint16_t method, const
e = za->entry + idx;
old_method = (e->orig == NULL ? ZIP_EM_NONE : e->orig->encryption_method);
if (method == old_method && password == NULL) {
if (e->changes) {
if (e->changes->changed & ZIP_DIRENT_PASSWORD) {
_zip_crypto_clear(e->changes->password, strlen(e->changes->password));
free(e->changes->password);
e->changes->password = (e->orig == NULL ? NULL : e->orig->password);
}
e->changes->changed &= ~(ZIP_DIRENT_ENCRYPTION_METHOD | ZIP_DIRENT_PASSWORD);
if (e->changes->changed == 0) {
_zip_dirent_free(e->changes);
e->changes = NULL;
}
if (e->changes == NULL) {
if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return -1;
}
}
if (password) {
if ((our_password = strdup(password)) == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return -1;
}
}
e->changes->encryption_method = method;
e->changes->changed |= ZIP_DIRENT_ENCRYPTION_METHOD;
if (password) {
e->changes->password = our_password;
e->changes->changed |= ZIP_DIRENT_PASSWORD;
}
else {
char *our_password = NULL;
if (password) {
if ((our_password = strdup(password)) == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return -1;
}
}
if (e->changes == NULL) {
if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) {
if (our_password) {
_zip_crypto_clear(our_password, strlen(our_password));
}
free(our_password);
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return -1;
}
}
e->changes->encryption_method = method;
e->changes->changed |= ZIP_DIRENT_ENCRYPTION_METHOD;
if (password) {
e->changes->password = our_password;
e->changes->changed |= ZIP_DIRENT_PASSWORD;
}
else {
if (e->changes->changed & ZIP_DIRENT_PASSWORD) {
_zip_crypto_clear(e->changes->password, strlen(e->changes->password));
free(e->changes->password);
e->changes->password = e->orig ? e->orig->password : NULL;
e->changes->changed &= ~ZIP_DIRENT_PASSWORD;
}
if (e->changes->changed & ZIP_DIRENT_PASSWORD) {
_zip_crypto_clear(e->changes->password, strlen(e->changes->password));
free(e->changes->password);
e->changes->password = e->orig ? e->orig->password : NULL;
e->changes->changed &= ~ZIP_DIRENT_PASSWORD;
}
}

View File

@@ -207,7 +207,7 @@ _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
_zip_error_copy(error, &za->error);
if (zip_error_code_zip(error) == ZIP_ER_NOZIP) {
if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
/* not a zip - find out if it's truncated */
if (_is_truncated_zip(src)) {
zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);

62
3rdparty/libzip/lib/zip_realloc.c vendored Normal file
View File

@@ -0,0 +1,62 @@
/*
zip_realloc.c -- reallocate with additional elements
Copyright (C) 2009-2025 Dieter Baron and Thomas Klausner
This file is part of libzip, a library to manipulate ZIP archives.
The authors can be contacted at <info@libzip.org>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. The names of the authors may not be used to endorse or promote
products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include "zipint.h"
bool zip_realloc(void **memory, zip_uint64_t *alloced_elements, zip_uint64_t element_size, zip_uint64_t additional_elements, zip_error_t *error) {
zip_uint64_t new_alloced_elements;
void *new_memory;
if (additional_elements == 0) {
return true;
}
new_alloced_elements = *alloced_elements + additional_elements;
if (new_alloced_elements < additional_elements || new_alloced_elements > SIZE_MAX / element_size) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
return false;
}
if ((new_memory = realloc(*memory, (size_t)(new_alloced_elements * element_size))) == NULL) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
return false;
}
*memory = new_memory;
*alloced_elements = new_alloced_elements;
return true;
}

View File

@@ -38,7 +38,6 @@
ZIP_EXTERN int
zip_set_file_compression(zip_t *za, zip_uint64_t idx, zip_int32_t method, zip_uint32_t flags) {
zip_entry_t *e;
zip_int32_t old_method;
if (idx >= za->nentry) {
zip_error_set(&za->error, ZIP_ER_INVAL, 0);
@@ -61,35 +60,21 @@ zip_set_file_compression(zip_t *za, zip_uint64_t idx, zip_int32_t method, zip_ui
e = za->entry + idx;
old_method = (e->orig == NULL ? ZIP_CM_DEFAULT : e->orig->comp_method);
/* TODO: do we want to recompress if level is set? Only if it's
* different than what bit flags tell us, but those are not
* defined for all compression methods, or not directly mappable
* to levels */
if (method == old_method) {
if (e->changes) {
e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD;
e->changes->compression_level = 0;
if (e->changes->changed == 0) {
_zip_dirent_free(e->changes);
e->changes = NULL;
}
if (e->changes == NULL) {
if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return -1;
}
}
else {
if (e->changes == NULL) {
if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return -1;
}
}
e->changes->comp_method = method;
e->changes->compression_level = (zip_uint16_t)flags;
e->changes->changed |= ZIP_DIRENT_COMP_METHOD;
}
e->changes->comp_method = method;
e->changes->compression_level = (zip_uint16_t)flags;
e->changes->changed |= ZIP_DIRENT_COMP_METHOD;
return 0;
}

View File

@@ -340,6 +340,7 @@ buffer_clone(buffer_t *buffer, zip_uint64_t offset, zip_error_t *error) {
fragment_offset = offset - buffer->fragment_offsets[fragment];
if (fragment_offset == 0) {
/* We can't be at beginning of fragment zero if offset > 0. */
fragment--;
fragment_offset = buffer->fragments[fragment].length;
}
@@ -427,32 +428,23 @@ buffer_free(buffer_t *buffer) {
static bool
buffer_grow_fragments(buffer_t *buffer, zip_uint64_t capacity, zip_error_t *error) {
zip_buffer_fragment_t *fragments;
zip_uint64_t *offsets;
zip_uint64_t additional_fragments;
zip_uint64_t offset_capacity = buffer->fragments_capacity + 1;
if (capacity < buffer->fragments_capacity) {
if (capacity <= buffer->fragments_capacity) {
return true;
}
zip_uint64_t fragments_size = sizeof(buffer->fragments[0]) * capacity;
zip_uint64_t offsets_size = sizeof(buffer->fragment_offsets[0]) * (capacity + 1);
additional_fragments = capacity - buffer->fragments_capacity;
if (capacity == ZIP_UINT64_MAX || fragments_size < capacity || fragments_size > SIZE_MAX|| offsets_size < capacity || offsets_size > SIZE_MAX) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
if (!ZIP_REALLOC(buffer->fragments, buffer->fragments_capacity, additional_fragments, error)) {
return false;
}
if ((fragments = realloc(buffer->fragments, (size_t)fragments_size)) == NULL) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
/* The size of both buffer->fragments and buffer->fragment_offsets is stored in buffer->fragments_capacity, so use a temporary capacity variable here for reallocating buffer->fragment_offsets. */
if (!ZIP_REALLOC(buffer->fragment_offsets, offset_capacity, additional_fragments, error)) {
buffer->fragments_capacity -= additional_fragments;
return false;
}
buffer->fragments = fragments;
if ((offsets = realloc(buffer->fragment_offsets, (size_t)offsets_size)) == NULL) {
zip_error_set(error, ZIP_ER_MEMORY, 0);
return false;
}
buffer->fragment_offsets = offsets;
buffer->fragments_capacity = capacity;
return true;
}

View File

@@ -44,6 +44,7 @@ struct context {
bool can_store;
bool is_stored; /* only valid if end_of_stream is true */
bool compress;
bool check_consistency;
zip_int32_t method;
zip_uint64_t size;
@@ -86,11 +87,10 @@ static size_t implementations_size = sizeof(implementations) / sizeof(implementa
static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags);
static zip_int64_t compress_callback(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
static void context_free(struct context *ctx);
static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm);
static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm, bool check_consistency);
static zip_int64_t compress_read(zip_source_t *, struct context *, void *, zip_uint64_t);
zip_compression_algorithm_t *
_zip_get_compression_algorithm(zip_int32_t method, bool compress) {
zip_compression_algorithm_t *_zip_get_compression_algorithm(zip_int32_t method, bool compress) {
size_t i;
zip_uint16_t real_method = ZIP_CM_ACTUAL(method);
@@ -108,16 +108,14 @@ _zip_get_compression_algorithm(zip_int32_t method, bool compress) {
return NULL;
}
ZIP_EXTERN int
zip_compression_method_supported(zip_int32_t method, int compress) {
ZIP_EXTERN int zip_compression_method_supported(zip_int32_t method, int compress) {
if (method == ZIP_CM_STORE) {
return 1;
}
return _zip_get_compression_algorithm(method, compress) != NULL;
}
zip_source_t *
zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, zip_uint32_t compression_flags) {
zip_source_t *zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, zip_uint32_t compression_flags) {
return compression_source_new(za, src, method, true, compression_flags);
}
@@ -127,8 +125,7 @@ zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t method) {
}
static zip_source_t *
compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags) {
static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags) {
struct context *ctx;
zip_source_t *s2;
zip_compression_algorithm_t *algorithm = NULL;
@@ -143,7 +140,7 @@ compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool co
return NULL;
}
if ((ctx = context_new(method, compress, compression_flags, algorithm)) == NULL) {
if ((ctx = context_new(method, compress, compression_flags, algorithm, za->open_flags & ZIP_CHECKCONS)) == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return NULL;
}
@@ -157,8 +154,7 @@ compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool co
}
static struct context *
context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm) {
static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm, bool check_consistency) {
struct context *ctx;
if ((ctx = (struct context *)malloc(sizeof(*ctx))) == NULL) {
@@ -172,6 +168,7 @@ context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, z
ctx->end_of_input = false;
ctx->end_of_stream = false;
ctx->is_stored = false;
ctx->check_consistency = check_consistency;
if ((ctx->ud = ctx->algorithm->allocate(ZIP_CM_ACTUAL(method), compression_flags, &ctx->error)) == NULL) {
zip_error_fini(&ctx->error);
@@ -228,7 +225,23 @@ compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t l
ctx->end_of_stream = true;
if (!ctx->end_of_input) {
/* TODO: garbage after stream, or compression ended before all data read */
n = zip_source_read(src, ctx->buffer, 1);
if (n < 0) {
zip_error_set_from_source(&ctx->error, src);
end = true;
break;
}
else if (n == 0) {
ctx->end_of_input = true;
n = ctx->algorithm->end_of_input(ctx->ud) ? 1 : 0;
}
if (n > 0 && ctx->check_consistency) {
/* garbage after stream, or compression ended before all data read */
zip_error_set(&ctx->error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMPRESSED_DATA_TRAILING_GARBAGE);
end = true;
break;
}
}
if (ctx->first_read < 0) {

View File

@@ -95,9 +95,11 @@ _zip_stdio_op_close(zip_source_file_context_t *ctx) {
zip_int64_t
_zip_stdio_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len) {
size_t i;
#if SIZE_MAX < ZIP_UINT64_MAX
if (len > SIZE_MAX) {
len = SIZE_MAX;
}
#endif
if ((i = fread(buf, 1, (size_t)len, ctx->f)) == 0) {
if (ferror((FILE *)ctx->f)) {

View File

@@ -33,13 +33,6 @@
#include "zip_source_file_win32.h"
/* ACL is not available when targeting the games API partition */
#if defined(WINAPI_FAMILY_PARTITION) && defined(WINAPI_PARTITION_GAMES)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_GAMES)
#define ACL_UNSUPPORTED
#endif
#endif
static zip_int64_t _zip_win32_named_op_commit_write(zip_source_file_context_t *ctx);
static zip_int64_t _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx);
static bool _zip_win32_named_op_open(zip_source_file_context_t *ctx);
@@ -106,29 +99,24 @@ _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx) {
zip_uint32_t value, i;
HANDLE th = INVALID_HANDLE_VALUE;
PSECURITY_DESCRIPTOR psd = NULL;
PSECURITY_ATTRIBUTES psa = NULL;
PSECURITY_DESCRIPTOR psd = NULL;
#ifdef HAVE_GETSECURITYINFO
SECURITY_ATTRIBUTES sa;
SECURITY_INFORMATION si;
DWORD success;
PACL dacl = NULL;
#endif
char *tempname = NULL;
size_t tempname_size = 0;
#ifdef HAVE_GETSECURITYINFO
if ((HANDLE)ctx->f != INVALID_HANDLE_VALUE && GetFileType((HANDLE)ctx->f) == FILE_TYPE_DISK) {
si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
#ifdef ACL_UNSUPPORTED
success = ERROR_NOT_SUPPORTED;
#else
success = GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, si, NULL, NULL, &dacl, NULL, &psd);
#endif
if (success == ERROR_SUCCESS) {
if (GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &psd) == ERROR_SUCCESS) {
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = psd;
psa = &sa;
}
}
#endif
#ifndef MS_UWP
value = GetTickCount();

View File

@@ -39,8 +39,7 @@ zip_file_attributes_init(zip_file_attributes_t *attributes) {
attributes->version = 1;
}
int
zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) {
int zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) {
if (src->source_closed) {
return -1;
}

View File

@@ -198,8 +198,10 @@ pkware_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, zip
zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
return -1;
}
attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED;
attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS;
attributes->version_needed = 20;
attributes->general_purpose_bit_flags = ZIP_GPBF_DATA_DESCRIPTOR;
attributes->general_purpose_bit_mask = ZIP_GPBF_DATA_DESCRIPTOR;
return 0;
}

View File

@@ -345,7 +345,7 @@ window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_sou
void
_zip_deregister_source(zip_t *za, zip_source_t *src) {
unsigned int i;
zip_uint64_t i;
for (i = 0; i < za->nopen_source; i++) {
if (za->open_source[i] == src) {
@@ -359,18 +359,10 @@ _zip_deregister_source(zip_t *za, zip_source_t *src) {
int
_zip_register_source(zip_t *za, zip_source_t *src) {
zip_source_t **open_source;
if (za->nopen_source + 1 >= za->nopen_source_alloc) {
unsigned int n;
n = za->nopen_source_alloc + 10;
open_source = (zip_source_t **)realloc(za->open_source, n * sizeof(zip_source_t *));
if (open_source == NULL) {
zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
if (!ZIP_REALLOC(za->open_source, za->nopen_source_alloc, 10, &za->error)) {
return -1;
}
za->nopen_source_alloc = n;
za->open_source = open_source;
}
za->open_source[za->nopen_source++] = src;

View File

@@ -102,11 +102,14 @@
/* according to unzip-6.0's zipinfo.c, this corresponds to a directory with rwx permissions for everyone */
#define ZIP_EXT_ATTRIB_DEFAULT_DIR (0040777u << 16)
#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x0836
/* Allowed: Encryption specific bits, data descriptor, compression specific, UTF-8 filename */
#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x083e
#define ZIP_MAX(a, b) ((a) > (b) ? (a) : (b))
#define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b))
#define ZIP_REALLOC(memory, alloced_elements, additional_elements, error) zip_realloc((void **)&memory, &alloced_elements, sizeof(*memory), additional_elements, error)
/* This section contains API that won't materialize like this. It's
placed in the internal section, pending cleanup. */
@@ -152,7 +155,7 @@ struct zip_compression_algorithm {
bool (*input)(void *ctx, zip_uint8_t *data, zip_uint64_t length);
/* all input data has been provided */
void (*end_of_input)(void *ctx);
bool (*end_of_input)(void *ctx);
/* process input data, writing to data, which has room for length bytes, update length to number of bytes written */
zip_compression_status_t (*process)(void *ctx, zip_uint8_t *data, zip_uint64_t *length);
@@ -241,6 +244,7 @@ extern const int _zip_err_details_count;
#define ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH 22 /* G EOCD64 and EOCD64 locator do not match */
#define ZIP_ER_DETAIL_UTF8_FILENAME_MISMATCH 23 /* E UTF-8 filename is ASCII and doesn't match filename */
#define ZIP_ER_DETAIL_UTF8_COMMENT_MISMATCH 24 /* E UTF-8 comment is ASCII and doesn't match comment */
#define ZIP_ER_DETAIL_COMPRESSED_DATA_TRAILING_GARBAGE 25 /* G garbage at end of compressed data */
/* directory entry: general purpose bit flags */
@@ -305,8 +309,8 @@ struct zip {
zip_uint64_t nentry_alloc; /* number of entries allocated */
zip_entry_t *entry; /* entries */
unsigned int nopen_source; /* number of open sources using archive */
unsigned int nopen_source_alloc; /* number of sources allocated */
zip_uint64_t nopen_source; /* number of open sources using archive */
zip_uint64_t nopen_source_alloc; /* number of sources allocated */
zip_source_t **open_source; /* open sources using archive */
zip_hash_t *names; /* hash table for name lookup */
@@ -552,13 +556,14 @@ zip_int64_t _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint6
time_t _zip_d2u_time(const zip_dostime_t*);
void _zip_deregister_source(zip_t *za, zip_source_t *src);
void _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool, zip_uint32_t);
bool _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool);
int zip_dirent_check_consistency(zip_dirent_t *dirent);
zip_dirent_t *_zip_dirent_clone(const zip_dirent_t *);
void _zip_dirent_free(zip_dirent_t *);
void _zip_dirent_finalize(zip_dirent_t *);
time_t zip_dirent_get_last_mod_mtime(zip_dirent_t *de);
void _zip_dirent_init(zip_dirent_t *);
bool _zip_dirent_merge(zip_dirent_t *de, zip_dirent_t *de_orig, bool replacing_data, zip_error_t *error);
bool _zip_dirent_needs_zip64(const zip_dirent_t *, zip_flags_t);
zip_dirent_t *_zip_dirent_new(void);
bool zip_dirent_process_ef_zip64(zip_dirent_t * zde, const zip_uint8_t * ef, zip_uint64_t got_len, bool local, zip_error_t * error);
@@ -618,6 +623,8 @@ int _zip_progress_start(zip_progress_t *progress);
int _zip_progress_subrange(zip_progress_t *progress, double start, double end);
int _zip_progress_update(zip_progress_t *progress, double value);
bool zip_realloc(void **memory, zip_uint64_t *alloced_elements, zip_uint64_t element_size, zip_uint64_t additional_elements, zip_error_t *error);
/* this symbol is extern so it can be overridden for regression testing */
ZIP_EXTERN bool zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length);
zip_uint32_t zip_random_uint32(void);

View File

@@ -86,6 +86,7 @@
<ClCompile Include="lib\zip_name_locate.c" />
<ClCompile Include="lib\zip_new.c" />
<ClCompile Include="lib\zip_open.c" />
<ClCompile Include="lib\zip_realloc.c" />
<ClCompile Include="lib\zip_pkware.c" />
<ClCompile Include="lib\zip_progress.c" />
<ClCompile Include="lib\zip_random_win32.c" />

View File

@@ -1,7 +1,7 @@
#define MY_VER_MAJOR 24
#define MY_VER_MINOR 8
#define MY_VER_MAJOR 25
#define MY_VER_MINOR 0
#define MY_VER_BUILD 0
#define MY_VERSION_NUMBERS "24.08"
#define MY_VERSION_NUMBERS "25.00"
#define MY_VERSION MY_VERSION_NUMBERS
#ifdef MY_CPU_NAME
@@ -10,12 +10,12 @@
#define MY_VERSION_CPU MY_VERSION
#endif
#define MY_DATE "2024-08-11"
#define MY_DATE "2025-07-05"
#undef MY_COPYRIGHT
#undef MY_VERSION_COPYRIGHT_DATE
#define MY_AUTHOR_NAME "Igor Pavlov"
#define MY_COPYRIGHT_PD "Igor Pavlov : Public domain"
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2024 Igor Pavlov"
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2025 Igor Pavlov"
#ifdef USE_COPYRIGHT_CR
#define MY_COPYRIGHT MY_COPYRIGHT_CR

View File

@@ -1,5 +1,5 @@
/* Compiler.h : Compiler specific defines and pragmas
2024-01-22 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_COMPILER_H
#define ZIP7_INC_COMPILER_H
@@ -183,6 +183,16 @@ typedef void (*Z7_void_Function)(void);
#define Z7_ATTRIB_NO_VECTORIZE
#endif
#if defined(Z7_MSC_VER_ORIGINAL) && (Z7_MSC_VER_ORIGINAL >= 1920)
#define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE _Pragma("optimize ( \"s\", on )")
#define Z7_PRAGMA_OPTIMIZE_DEFAULT _Pragma("optimize ( \"\", on )")
#else
#define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE
#define Z7_PRAGMA_OPTIMIZE_DEFAULT
#endif
#if defined(MY_CPU_X86_OR_AMD64) && ( \
defined(__clang__) && (__clang_major__ >= 4) \
|| defined(__GNUC__) && (__GNUC__ >= 5))

View File

@@ -1,5 +1,5 @@
/* CpuArch.h -- CPU specific code
2024-06-17 : Igor Pavlov : Public domain */
Igor Pavlov : Public domain */
#ifndef ZIP7_INC_CPU_ARCH_H
#define ZIP7_INC_CPU_ARCH_H
@@ -47,6 +47,12 @@ MY_CPU_64BIT means that processor can work with 64-bit registers.
#define MY_CPU_SIZEOF_POINTER 4
#endif
#if defined(__SSE2__) \
|| defined(MY_CPU_AMD64) \
|| defined(_M_IX86_FP) && (_M_IX86_FP >= 2)
#define MY_CPU_SSE2
#endif
#if defined(_M_ARM64) \
|| defined(_M_ARM64EC) \
@@ -509,11 +515,19 @@ problem-4 : performace:
#if defined(MY_CPU_LE_UNALIGN) && defined(Z7_CPU_FAST_BSWAP_SUPPORTED)
#if 0
// Z7_BSWAP16 can be slow for x86-msvc
#define GetBe16_to32(p) (Z7_BSWAP16 (*(const UInt16 *)(const void *)(p)))
#else
#define GetBe16_to32(p) (Z7_BSWAP32 (*(const UInt16 *)(const void *)(p)) >> 16)
#endif
#define GetBe32(p) Z7_BSWAP32 (*(const UInt32 *)(const void *)(p))
#define SetBe32(p, v) { (*(UInt32 *)(void *)(p)) = Z7_BSWAP32(v); }
#if defined(MY_CPU_LE_UNALIGN_64)
#define GetBe64(p) Z7_BSWAP64 (*(const UInt64 *)(const void *)(p))
#define SetBe64(p, v) { (*(UInt64 *)(void *)(p)) = Z7_BSWAP64(v); }
#endif
#else
@@ -536,21 +550,39 @@ problem-4 : performace:
#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4))
#endif
#ifndef SetBe64
#define SetBe64(p, v) { Byte *_ppp_ = (Byte *)(p); UInt64 _vvv_ = (v); \
_ppp_[0] = (Byte)(_vvv_ >> 56); \
_ppp_[1] = (Byte)(_vvv_ >> 48); \
_ppp_[2] = (Byte)(_vvv_ >> 40); \
_ppp_[3] = (Byte)(_vvv_ >> 32); \
_ppp_[4] = (Byte)(_vvv_ >> 24); \
_ppp_[5] = (Byte)(_vvv_ >> 16); \
_ppp_[6] = (Byte)(_vvv_ >> 8); \
_ppp_[7] = (Byte)_vvv_; }
#endif
#ifndef GetBe16
#ifdef GetBe16_to32
#define GetBe16(p) ( (UInt16) GetBe16_to32(p))
#else
#define GetBe16(p) ( (UInt16) ( \
((UInt16)((const Byte *)(p))[0] << 8) | \
((const Byte *)(p))[1] ))
#endif
#endif
#if defined(MY_CPU_BE)
#define Z7_CONV_BE_TO_NATIVE_CONST32(v) (v)
#define Z7_CONV_LE_TO_NATIVE_CONST32(v) Z7_BSWAP32_CONST(v)
#define Z7_CONV_NATIVE_TO_BE_32(v) (v)
// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1) ((b1) | ((b0) << 8))
#elif defined(MY_CPU_LE)
#define Z7_CONV_BE_TO_NATIVE_CONST32(v) Z7_BSWAP32_CONST(v)
#define Z7_CONV_LE_TO_NATIVE_CONST32(v) (v)
#define Z7_CONV_NATIVE_TO_BE_32(v) Z7_BSWAP32(v)
// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1) ((b0) | ((b1) << 8))
#else
#error Stop_Compiling_Unknown_Endian_CONV
#endif
@@ -589,6 +621,11 @@ problem-4 : performace:
#endif
#ifndef GetBe16_to32
#define GetBe16_to32(p) GetBe16(p)
#endif
#if defined(MY_CPU_X86_OR_AMD64) \
|| defined(MY_CPU_ARM_OR_ARM64) \
|| defined(MY_CPU_PPC_OR_PPC64)
@@ -617,6 +654,7 @@ BoolInt CPU_IsSupported_SSE2(void);
BoolInt CPU_IsSupported_SSSE3(void);
BoolInt CPU_IsSupported_SSE41(void);
BoolInt CPU_IsSupported_SHA(void);
BoolInt CPU_IsSupported_SHA512(void);
BoolInt CPU_IsSupported_PageGB(void);
#elif defined(MY_CPU_ARM_OR_ARM64)
@@ -634,6 +672,7 @@ BoolInt CPU_IsSupported_SHA1(void);
BoolInt CPU_IsSupported_SHA2(void);
BoolInt CPU_IsSupported_AES(void);
#endif
BoolInt CPU_IsSupported_SHA512(void);
#endif

View File

@@ -1,5 +1,5 @@
/* LzFindMt.h -- multithreaded Match finder for LZ algorithms
2024-01-22 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_LZ_FIND_MT_H
#define ZIP7_INC_LZ_FIND_MT_H
@@ -12,8 +12,10 @@ EXTERN_C_BEGIN
typedef struct
{
UInt32 numProcessedBlocks;
CThread thread;
Int32 affinityGroup;
UInt64 affinityInGroup;
UInt64 affinity;
CThread thread;
BoolInt wasCreated;
BoolInt needStart;

View File

@@ -18,6 +18,7 @@ typedef struct
int numBlockThreads_Reduced;
int numBlockThreads_Max;
int numTotalThreads;
unsigned numThreadGroups; // 0 : no groups
} CLzma2EncProps;
void Lzma2EncProps_Init(CLzma2EncProps *p);

View File

@@ -1,5 +1,5 @@
/* LzmaEnc.h -- LZMA Encoder
2023-04-13 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_LZMA_ENC_H
#define ZIP7_INC_LZMA_ENC_H
@@ -29,11 +29,13 @@ typedef struct
int numThreads; /* 1 or 2, default = 2 */
// int _pad;
Int32 affinityGroup;
UInt64 reduceSize; /* estimated size of data that will be compressed. default = (UInt64)(Int64)-1.
Encoder uses this value to reduce dictionary size */
UInt64 affinity;
UInt64 affinityInGroup;
} CLzmaEncProps;
void LzmaEncProps_Init(CLzmaEncProps *p);

View File

@@ -1,5 +1,5 @@
/* MtCoder.h -- Multi-thread Coder
2023-04-13 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_MT_CODER_H
#define ZIP7_INC_MT_CODER_H
@@ -16,7 +16,7 @@ EXTERN_C_BEGIN
#ifndef Z7_ST
#define MTCODER_GET_NUM_BLOCKS_FROM_THREADS(numThreads) ((numThreads) + (numThreads) / 8 + 1)
#define MTCODER_THREADS_MAX 64
#define MTCODER_THREADS_MAX 256
#define MTCODER_BLOCKS_MAX (MTCODER_GET_NUM_BLOCKS_FROM_THREADS(MTCODER_THREADS_MAX) + 3)
#else
#define MTCODER_THREADS_MAX 1
@@ -77,6 +77,7 @@ typedef struct CMtCoder_
size_t blockSize; /* size of input block */
unsigned numThreadsMax;
unsigned numThreadGroups;
UInt64 expectedDataSize;
ISeqInStreamPtr inStream;
@@ -125,6 +126,8 @@ typedef struct CMtCoder_
CMtProgress mtProgress;
CMtCoderBlock blocks[MTCODER_BLOCKS_MAX];
CMtCoderThread threads[MTCODER_THREADS_MAX];
CThreadNextGroup nextGroup;
} CMtCoder;

View File

@@ -1,5 +1,5 @@
/* Sha256.h -- SHA-256 Hash
2023-04-02 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_SHA256_H
#define ZIP7_INC_SHA256_H
@@ -14,6 +14,9 @@ EXTERN_C_BEGIN
#define SHA256_BLOCK_SIZE (SHA256_NUM_BLOCK_WORDS * 4)
#define SHA256_DIGEST_SIZE (SHA256_NUM_DIGEST_WORDS * 4)
typedef void (Z7_FASTCALL *SHA256_FUNC_UPDATE_BLOCKS)(UInt32 state[8], const Byte *data, size_t numBlocks);
/*
@@ -32,9 +35,16 @@ typedef void (Z7_FASTCALL *SHA256_FUNC_UPDATE_BLOCKS)(UInt32 state[8], const Byt
typedef struct
{
SHA256_FUNC_UPDATE_BLOCKS func_UpdateBlocks;
UInt64 count;
UInt64 _pad_2[2];
union
{
struct
{
SHA256_FUNC_UPDATE_BLOCKS func_UpdateBlocks;
UInt64 count;
} vars;
UInt64 _pad_64bit[4];
void *_pad_align_ptr[2];
} v;
UInt32 state[SHA256_NUM_DIGEST_WORDS];
Byte buffer[SHA256_BLOCK_SIZE];

View File

@@ -1,5 +1,5 @@
/* Sort.h -- Sort functions
2023-03-05 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_SORT_H
#define ZIP7_INC_SORT_H
@@ -8,10 +8,7 @@
EXTERN_C_BEGIN
void HeapSort(UInt32 *p, size_t size);
void HeapSort64(UInt64 *p, size_t size);
/* void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size); */
void Z7_FASTCALL HeapSort(UInt32 *p, size_t size);
EXTERN_C_END

View File

@@ -1,5 +1,5 @@
/* Threads.h -- multithreading library
2024-03-28 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_THREADS_H
#define ZIP7_INC_THREADS_H
@@ -140,12 +140,22 @@ WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param
WRes Thread_Wait_Close(CThread *p);
#ifdef _WIN32
WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinityMask);
#define Thread_Create_With_CpuSet(p, func, param, cs) \
Thread_Create_With_Affinity(p, func, param, *cs)
#else
WRes Thread_Create_With_CpuSet(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, const CCpuSet *cpuSet);
#endif
typedef struct
{
unsigned NumGroups;
unsigned NextGroup;
} CThreadNextGroup;
void ThreadNextGroup_Init(CThreadNextGroup *p, unsigned numGroups, unsigned startGroup);
unsigned ThreadNextGroup_GetNext(CThreadNextGroup *p);
#ifdef _WIN32

View File

@@ -1,5 +1,5 @@
/* Xz.h - Xz interface
2024-01-26 : Igor Pavlov : Public domain */
Igor Pavlov : Public domain */
#ifndef ZIP7_INC_XZ_H
#define ZIP7_INC_XZ_H
@@ -121,6 +121,7 @@ typedef struct
UInt64 startOffset;
} CXzStream;
#define Xz_CONSTRUCT(p) { (p)->numBlocks = 0; (p)->blocks = NULL; (p)->flags = 0; }
void Xz_Construct(CXzStream *p);
void Xz_Free(CXzStream *p, ISzAllocPtr alloc);
@@ -136,8 +137,13 @@ typedef struct
CXzStream *streams;
} CXzs;
#define Xzs_CONSTRUCT(p) { (p)->num = 0; (p)->numAllocated = 0; (p)->streams = NULL; }
void Xzs_Construct(CXzs *p);
void Xzs_Free(CXzs *p, ISzAllocPtr alloc);
/*
Xzs_ReadBackward() must be called for empty CXzs object.
Xzs_ReadBackward() can return non empty object with (p->num != 0) even in case of error.
*/
SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr inStream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc);
UInt64 Xzs_GetNumBlocks(const CXzs *p);
@@ -268,8 +274,8 @@ typedef struct
size_t outBufSize;
size_t outDataWritten; // the size of data in (outBuf) that were fully unpacked
Byte shaDigest[SHA256_DIGEST_SIZE];
Byte buf[XZ_BLOCK_HEADER_SIZE_MAX];
UInt32 shaDigest32[SHA256_DIGEST_SIZE / 4];
Byte buf[XZ_BLOCK_HEADER_SIZE_MAX]; // it must be aligned for 4-bytes
} CXzUnpacker;
/* alloc : aligned for cache line allocation is better */

View File

@@ -1,5 +1,5 @@
/* XzEnc.h -- Xz Encode
2023-04-13 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_XZ_ENC_H
#define ZIP7_INC_XZ_ENC_H
@@ -31,6 +31,7 @@ typedef struct
CLzma2EncProps lzma2Props;
CXzFilterProps filterProps;
unsigned checkId;
unsigned numThreadGroups; // 0 : no groups
UInt64 blockSize;
int numBlockThreads_Reduced;
int numBlockThreads_Max;

View File

@@ -1,5 +1,5 @@
/* 7zDec.c -- Decoding from 7z folder
2024-03-01 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -312,8 +312,9 @@ static BoolInt IS_MAIN_METHOD(UInt32 m)
case k_PPMD:
#endif
return True;
default:
return False;
}
return False;
}
static BoolInt IS_SUPPORTED_CODER(const CSzCoderInfo *c)

View File

@@ -1,5 +1,5 @@
/* AesOpt.c -- AES optimized code for x86 AES hardware instructions
2024-03-01 : Igor Pavlov : Public domain */
Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -80,19 +80,39 @@ AES_FUNC_START (name)
#define MM_XOR( dest, src) MM_OP(_mm_xor_si128, dest, src)
#if 1
// use aligned SSE load/store for data.
// It is required for our Aes functions, that data is aligned for 16-bytes.
// So we can use this branch of code.
// and compiler can use fused load-op SSE instructions:
// xorps xmm0, XMMWORD PTR [rdx]
#define LOAD_128(pp) (*(__m128i *)(void *)(pp))
#define STORE_128(pp, _v) *(__m128i *)(void *)(pp) = _v
// use aligned SSE load/store for data. Alternative code with direct access
// #define LOAD_128(pp) _mm_load_si128(pp)
// #define STORE_128(pp, _v) _mm_store_si128(pp, _v)
#else
// use unaligned load/store for data: movdqu XMMWORD PTR [rdx]
#define LOAD_128(pp) _mm_loadu_si128(pp)
#define STORE_128(pp, _v) _mm_storeu_si128(pp, _v)
#endif
AES_FUNC_START2 (AesCbc_Encode_HW)
{
if (numBlocks == 0)
return;
{
__m128i *p = (__m128i *)(void *)ivAes;
__m128i *data = (__m128i *)(void *)data8;
__m128i m = *p;
const __m128i k0 = p[2];
const __m128i k1 = p[3];
const UInt32 numRounds2 = *(const UInt32 *)(p + 1) - 1;
for (; numBlocks != 0; numBlocks--, data++)
do
{
UInt32 r = numRounds2;
const __m128i *w = p + 4;
__m128i temp = *data;
__m128i temp = LOAD_128(data);
MM_XOR (temp, k0)
MM_XOR (m, temp)
MM_OP_m (_mm_aesenc_si128, k1)
@@ -104,9 +124,12 @@ AES_FUNC_START2 (AesCbc_Encode_HW)
}
while (--r);
MM_OP_m (_mm_aesenclast_si128, w[0])
*data = m;
STORE_128(data, m);
data++;
}
while (--numBlocks);
*p = m;
}
}
@@ -139,12 +162,12 @@ AES_FUNC_START2 (AesCbc_Encode_HW)
#define WOP(op) op (m0, 0) WOP_M1(op)
#define DECLARE_VAR(reg, ii) __m128i reg;
#define LOAD_data( reg, ii) reg = data[ii];
#define STORE_data( reg, ii) data[ii] = reg;
#define LOAD_data_ii(ii) LOAD_128(data + (ii))
#define LOAD_data( reg, ii) reg = LOAD_data_ii(ii);
#define STORE_data( reg, ii) STORE_128(data + (ii), reg);
#if (NUM_WAYS > 1)
#define XOR_data_M1(reg, ii) MM_XOR (reg, data[ii- 1])
#define XOR_data_M1(reg, ii) MM_XOR (reg, LOAD_128(data + (ii- 1)))
#endif
#define MM_OP_key(op, reg) MM_OP(op, reg, key);
@@ -156,25 +179,22 @@ AES_FUNC_START2 (AesCbc_Encode_HW)
#define AES_XOR( reg, ii) MM_OP_key (_mm_xor_si128, reg)
#define CTR_START(reg, ii) MM_OP (_mm_add_epi64, ctr, one) reg = ctr;
#define CTR_END( reg, ii) MM_XOR (data[ii], reg)
#define CTR_END( reg, ii) STORE_128(data + (ii), _mm_xor_si128(reg, \
LOAD_128 (data + (ii))));
#define WOP_KEY(op, n) { \
const __m128i key = w[n]; \
WOP(op); }
WOP(op) }
#define WIDE_LOOP_START \
dataEnd = data + numBlocks; \
if (numBlocks >= NUM_WAYS) \
{ dataEnd -= NUM_WAYS; do { \
#define WIDE_LOOP_END \
data += NUM_WAYS; \
} while (data <= dataEnd); \
dataEnd += NUM_WAYS; } \
#define SINGLE_LOOP \
for (; data < dataEnd; data++)
@@ -184,54 +204,73 @@ AES_FUNC_START2 (AesCbc_Encode_HW)
#define AVX_XOR(dest, src) MM_OP(_mm256_xor_si256, dest, src)
#define AVX_DECLARE_VAR(reg, ii) __m256i reg;
#define AVX_LOAD_data( reg, ii) reg = ((const __m256i *)(const void *)data)[ii];
#define AVX_STORE_data( reg, ii) ((__m256i *)(void *)data)[ii] = reg;
#if 1
// use unaligned AVX load/store for data.
// It is required for our Aes functions, that data is aligned for 16-bytes.
// But we need 32-bytes reading.
// So we use intrinsics for unaligned AVX load/store.
// notes for _mm256_storeu_si256:
// msvc2022: uses vmovdqu and keeps the order of instruction sequence.
// new gcc11 uses vmovdqu
// old gcc9 could use pair of instructions:
// vmovups %xmm7, -224(%rax)
// vextracti128 $0x1, %ymm7, -208(%rax)
#define AVX_LOAD(p) _mm256_loadu_si256((const __m256i *)(const void *)(p))
#define AVX_STORE(p, _v) _mm256_storeu_si256((__m256i *)(void *)(p), _v);
#else
// use aligned AVX load/store for data.
// for debug: we can use this branch, if we are sure that data is aligned for 32-bytes.
// msvc2022 uses vmovdqu still
// gcc uses vmovdqa (that requires 32-bytes alignment)
#define AVX_LOAD(p) (*(const __m256i *)(const void *)(p))
#define AVX_STORE(p, _v) (*(__m256i *)(void *)(p)) = _v;
#endif
#define AVX_LOAD_data( reg, ii) reg = AVX_LOAD((const __m256i *)(const void *)data + (ii));
#define AVX_STORE_data( reg, ii) AVX_STORE((__m256i *)(void *)data + (ii), reg)
/*
AVX_XOR_data_M1() needs unaligned memory load
if (we don't use _mm256_loadu_si256() here)
{
Most compilers with enabled optimizations generate fused AVX (LOAD + OP)
instruction that can load unaligned data.
But GCC and CLANG without -O2 or -O1 optimizations can generate separated
LOAD-ALIGNED (vmovdqa) instruction that will fail on execution.
}
Note: some compilers generate more instructions, if we use _mm256_loadu_si256() here.
v23.02: we use _mm256_loadu_si256() here, because we need compatibility with any compiler.
AVX_XOR_data_M1() needs unaligned memory load, even if (data)
is aligned for 256-bits, because we read 32-bytes chunk that
crosses (data) position: from (data - 16bytes) to (data + 16bytes).
*/
#define AVX_XOR_data_M1(reg, ii) AVX_XOR (reg, _mm256_loadu_si256(&(((const __m256i *)(const void *)(data - 1))[ii])))
// for debug only: the following code will fail on execution, if compiled by some compilers:
// #define AVX_XOR_data_M1(reg, ii) AVX_XOR (reg, (((const __m256i *)(const void *)(data - 1))[ii]))
#define AVX_XOR_data_M1(reg, ii) AVX_XOR (reg, _mm256_loadu_si256((const __m256i *)(const void *)(data - 1) + (ii)))
#define AVX_AES_DEC( reg, ii) MM_OP_key (_mm256_aesdec_epi128, reg)
#define AVX_AES_DEC_LAST( reg, ii) MM_OP_key (_mm256_aesdeclast_epi128, reg)
#define AVX_AES_ENC( reg, ii) MM_OP_key (_mm256_aesenc_epi128, reg)
#define AVX_AES_ENC_LAST( reg, ii) MM_OP_key (_mm256_aesenclast_epi128, reg)
#define AVX_AES_XOR( reg, ii) MM_OP_key (_mm256_xor_si256, reg)
#define AVX_CTR_START(reg, ii) MM_OP (_mm256_add_epi64, ctr2, two) reg = _mm256_xor_si256(ctr2, key);
#define AVX_CTR_END( reg, ii) AVX_XOR (((__m256i *)(void *)data)[ii], reg)
#define AVX_CTR_START(reg, ii) \
MM_OP (_mm256_add_epi64, ctr2, two) \
reg = _mm256_xor_si256(ctr2, key);
#define AVX_CTR_END(reg, ii) \
AVX_STORE((__m256i *)(void *)data + (ii), _mm256_xor_si256(reg, \
AVX_LOAD ((__m256i *)(void *)data + (ii))));
#define AVX_WOP_KEY(op, n) { \
const __m256i key = w[n]; \
WOP(op); }
WOP(op) }
#define NUM_AES_KEYS_MAX 15
#define WIDE_LOOP_START_AVX(OP) \
dataEnd = data + numBlocks; \
if (numBlocks >= NUM_WAYS * 2) \
{ __m256i keys[NUM_AES_KEYS_MAX]; \
UInt32 ii; \
OP \
for (ii = 0; ii < numRounds; ii++) \
keys[ii] = _mm256_broadcastsi128_si256(p[ii]); \
dataEnd -= NUM_WAYS * 2; do { \
{ __m256i keys[NUM_AES_KEYS_MAX]; \
OP \
{ UInt32 ii; for (ii = 0; ii < numRounds; ii++) \
keys[ii] = _mm256_broadcastsi128_si256(p[ii]); } \
dataEnd -= NUM_WAYS * 2; \
do { \
#define WIDE_LOOP_END_AVX(OP) \
data += NUM_WAYS * 2; \
} while (data <= dataEnd); \
dataEnd += NUM_WAYS * 2; \
OP \
_mm256_zeroupper(); \
data += NUM_WAYS * 2; \
} while (data <= dataEnd); \
dataEnd += NUM_WAYS * 2; \
OP \
_mm256_zeroupper(); \
} \
/* MSVC for x86: If we don't call _mm256_zeroupper(), and -arch:IA32 is not specified,
@@ -246,21 +285,20 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
__m128i *p = (__m128i *)(void *)ivAes;
__m128i *data = (__m128i *)(void *)data8;
__m128i iv = *p;
const __m128i *wStart = p + *(const UInt32 *)(p + 1) * 2 + 2 - 1;
const __m128i * const wStart = p + (size_t)*(const UInt32 *)(p + 1) * 2 + 2 - 1;
const __m128i *dataEnd;
p += 2;
WIDE_LOOP_START
{
const __m128i *w = wStart;
WOP (DECLARE_VAR)
WOP (LOAD_data)
WOP_KEY (AES_XOR, 1)
do
{
WOP_KEY (AES_DEC, 0)
w--;
}
while (w != p);
@@ -268,7 +306,7 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
MM_XOR (m0, iv)
WOP_M1 (XOR_data_M1)
iv = data[NUM_WAYS - 1];
LOAD_data(iv, NUM_WAYS - 1)
WOP (STORE_data)
}
WIDE_LOOP_END
@@ -276,7 +314,8 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
SINGLE_LOOP
{
const __m128i *w = wStart - 1;
__m128i m = _mm_xor_si128 (w[2], *data);
__m128i m = _mm_xor_si128 (w[2], LOAD_data_ii(0));
do
{
MM_OP_m (_mm_aesdec_si128, w[1])
@@ -286,10 +325,9 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
while (w != p);
MM_OP_m (_mm_aesdec_si128, w[1])
MM_OP_m (_mm_aesdeclast_si128, w[0])
MM_XOR (m, iv)
iv = *data;
*data = m;
LOAD_data(iv, 0)
STORE_data(m, 0)
}
p[-2] = iv;
@@ -301,9 +339,9 @@ AES_FUNC_START2 (AesCtr_Code_HW)
__m128i *p = (__m128i *)(void *)ivAes;
__m128i *data = (__m128i *)(void *)data8;
__m128i ctr = *p;
UInt32 numRoundsMinus2 = *(const UInt32 *)(p + 1) * 2 - 1;
const UInt32 numRoundsMinus2 = *(const UInt32 *)(p + 1) * 2 - 1;
const __m128i *dataEnd;
__m128i one = _mm_cvtsi32_si128(1);
const __m128i one = _mm_cvtsi32_si128(1);
p += 2;
@@ -322,7 +360,6 @@ AES_FUNC_START2 (AesCtr_Code_HW)
}
while (--r);
WOP_KEY (AES_ENC_LAST, 0)
WOP (CTR_END)
}
WIDE_LOOP_END
@@ -344,7 +381,7 @@ AES_FUNC_START2 (AesCtr_Code_HW)
while (--numRounds2);
MM_OP_m (_mm_aesenc_si128, w[0])
MM_OP_m (_mm_aesenclast_si128, w[1])
MM_XOR (*data, m)
CTR_END (m, 0)
}
p[-2] = ctr;
@@ -421,7 +458,7 @@ VAES_FUNC_START2 (AesCbc_Decode_HW_256)
__m128i *data = (__m128i *)(void *)data8;
__m128i iv = *p;
const __m128i *dataEnd;
UInt32 numRounds = *(const UInt32 *)(p + 1) * 2 + 1;
const UInt32 numRounds = *(const UInt32 *)(p + 1) * 2 + 1;
p += 2;
WIDE_LOOP_START_AVX(;)
@@ -440,17 +477,17 @@ VAES_FUNC_START2 (AesCbc_Decode_HW_256)
while (w != keys);
AVX_WOP_KEY (AVX_AES_DEC_LAST, 0)
AVX_XOR (m0, _mm256_setr_m128i(iv, data[0]))
AVX_XOR (m0, _mm256_setr_m128i(iv, LOAD_data_ii(0)))
WOP_M1 (AVX_XOR_data_M1)
iv = data[NUM_WAYS * 2 - 1];
LOAD_data (iv, NUM_WAYS * 2 - 1)
WOP (AVX_STORE_data)
}
WIDE_LOOP_END_AVX(;)
SINGLE_LOOP
{
const __m128i *w = p + *(const UInt32 *)(p + 1 - 2) * 2 + 1 - 3;
__m128i m = _mm_xor_si128 (w[2], *data);
const __m128i *w = p - 2 + (size_t)*(const UInt32 *)(p + 1 - 2) * 2;
__m128i m = _mm_xor_si128 (w[2], LOAD_data_ii(0));
do
{
MM_OP_m (_mm_aesdec_si128, w[1])
@@ -462,8 +499,8 @@ VAES_FUNC_START2 (AesCbc_Decode_HW_256)
MM_OP_m (_mm_aesdeclast_si128, w[0])
MM_XOR (m, iv)
iv = *data;
*data = m;
LOAD_data(iv, 0)
STORE_data(m, 0)
}
p[-2] = iv;
@@ -493,9 +530,9 @@ VAES_FUNC_START2 (AesCtr_Code_HW_256)
__m128i *p = (__m128i *)(void *)ivAes;
__m128i *data = (__m128i *)(void *)data8;
__m128i ctr = *p;
UInt32 numRounds = *(const UInt32 *)(p + 1) * 2 + 1;
const UInt32 numRounds = *(const UInt32 *)(p + 1) * 2 + 1;
const __m128i *dataEnd;
__m128i one = _mm_cvtsi32_si128(1);
const __m128i one = _mm_cvtsi32_si128(1);
__m256i ctr2, two;
p += 2;
@@ -536,7 +573,7 @@ VAES_FUNC_START2 (AesCtr_Code_HW_256)
while (--numRounds2);
MM_OP_m (_mm_aesenc_si128, w[0])
MM_OP_m (_mm_aesenclast_si128, w[1])
MM_XOR (*data, m)
CTR_END (m, 0)
}
p[-2] = ctr;
@@ -731,9 +768,14 @@ AES_FUNC_START (name)
AES_FUNC_START2 (AesCbc_Encode_HW)
{
v128 * const p = (v128*)(void*)ivAes;
v128 *data = (v128*)(void*)data8;
if (numBlocks == 0)
return;
{
v128 * const p = (v128 *)(void *)ivAes;
v128 *data = (v128 *)(void *)data8;
v128 m = *p;
const UInt32 numRounds2 = *(const UInt32 *)(p + 1);
const v128 *w = p + (size_t)numRounds2 * 2;
const v128 k0 = p[2];
const v128 k1 = p[3];
const v128 k2 = p[4];
@@ -744,11 +786,14 @@ AES_FUNC_START2 (AesCbc_Encode_HW)
const v128 k7 = p[9];
const v128 k8 = p[10];
const v128 k9 = p[11];
const UInt32 numRounds2 = *(const UInt32 *)(p + 1);
const v128 *w = p + ((size_t)numRounds2 * 2);
const v128 k_z4 = w[-2];
const v128 k_z3 = w[-1];
const v128 k_z2 = w[0];
const v128 k_z1 = w[1];
const v128 k_z0 = w[2];
for (; numBlocks != 0; numBlocks--, data++)
// we don't use optimization veorq_u8(*data, k_z0) that can reduce one cycle,
// because gcc/clang compilers are not good for that optimization.
do
{
MM_XOR_m (*data)
AES_E_MC_m (k0)
@@ -757,24 +802,26 @@ AES_FUNC_START2 (AesCbc_Encode_HW)
AES_E_MC_m (k3)
AES_E_MC_m (k4)
AES_E_MC_m (k5)
AES_E_MC_m (k6)
AES_E_MC_m (k7)
AES_E_MC_m (k8)
if (numRounds2 >= 6)
{
AES_E_MC_m (k9)
AES_E_MC_m (p[12])
AES_E_MC_m (k6)
AES_E_MC_m (k7)
if (numRounds2 != 6)
{
AES_E_MC_m (p[13])
AES_E_MC_m (p[14])
AES_E_MC_m (k8)
AES_E_MC_m (k9)
}
}
AES_E_m (k_z1)
MM_XOR_m (k_z0)
*data = m;
AES_E_MC_m (k_z4)
AES_E_MC_m (k_z3)
AES_E_MC_m (k_z2)
AES_E_m (k_z1)
MM_XOR_m (k_z0)
*data++ = m;
}
while (--numBlocks);
*p = m;
}
}
@@ -834,10 +881,10 @@ AES_FUNC_START2 (AesCbc_Encode_HW)
AES_FUNC_START2 (AesCbc_Decode_HW)
{
v128 *p = (v128*)(void*)ivAes;
v128 *data = (v128*)(void*)data8;
v128 *p = (v128 *)(void *)ivAes;
v128 *data = (v128 *)(void *)data8;
v128 iv = *p;
const v128 *wStart = p + ((size_t)*(const UInt32 *)(p + 1)) * 2;
const v128 * const wStart = p + (size_t)*(const UInt32 *)(p + 1) * 2;
const v128 *dataEnd;
p += 2;
@@ -858,7 +905,7 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
WOP_KEY (AES_XOR, 0)
MM_XOR (m0, iv)
WOP_M1 (XOR_data_M1)
iv = data[NUM_WAYS - 1];
LOAD_data(iv, NUM_WAYS - 1)
WOP (STORE_data)
}
WIDE_LOOP_END
@@ -866,7 +913,7 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
SINGLE_LOOP
{
const v128 *w = wStart;
v128 m = *data;
v128 m; LOAD_data(m, 0)
AES_D_IMC_m (w[2])
do
{
@@ -878,8 +925,8 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
AES_D_m (w[1])
MM_XOR_m (w[0])
MM_XOR_m (iv)
iv = *data;
*data = m;
LOAD_data(iv, 0)
STORE_data(m, 0)
}
p[-2] = iv;
@@ -888,19 +935,17 @@ AES_FUNC_START2 (AesCbc_Decode_HW)
AES_FUNC_START2 (AesCtr_Code_HW)
{
v128 *p = (v128*)(void*)ivAes;
v128 *data = (v128*)(void*)data8;
v128 *p = (v128 *)(void *)ivAes;
v128 *data = (v128 *)(void *)data8;
uint64x2_t ctr = vreinterpretq_u64_u8(*p);
const v128 *wEnd = p + ((size_t)*(const UInt32 *)(p + 1)) * 2;
const v128 * const wEnd = p + (size_t)*(const UInt32 *)(p + 1) * 2;
const v128 *dataEnd;
uint64x2_t one = vdupq_n_u64(0);
// the bug in clang:
// __builtin_neon_vsetq_lane_i64(__s0, (int8x16_t)__s1, __p2);
#if defined(__clang__) && (__clang_major__ <= 9)
#pragma GCC diagnostic ignored "-Wvector-conversion"
#endif
one = vsetq_lane_u64(1, one, 0);
const uint64x2_t one = vsetq_lane_u64(1, vdupq_n_u64(0), 0);
p += 2;
WIDE_LOOP_START

View File

@@ -1,5 +1,5 @@
/* CpuArch.c -- CPU specific code
2024-07-04 : Igor Pavlov : Public domain */
Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -17,7 +17,7 @@
/*
cpuid instruction supports (subFunction) parameter in ECX,
that is used only with some specific (function) parameter values.
But we always use only (subFunction==0).
most functions use only (subFunction==0).
*/
/*
__cpuid(): MSVC and GCC/CLANG use same function/macro name
@@ -49,43 +49,49 @@
#if defined(MY_CPU_AMD64) && defined(__PIC__) \
&& ((defined (__GNUC__) && (__GNUC__ < 5)) || defined(__clang__))
#define x86_cpuid_MACRO(p, func) { \
/* "=&r" selects free register. It can select even rbx, if that register is free.
"=&D" for (RDI) also works, but the code can be larger with "=&D"
"2"(subFun) : 2 is (zero-based) index in the output constraint list "=c" (ECX). */
#define x86_cpuid_MACRO_2(p, func, subFunc) { \
__asm__ __volatile__ ( \
ASM_LN "mov %%rbx, %q1" \
ASM_LN "cpuid" \
ASM_LN "xchg %%rbx, %q1" \
: "=a" ((p)[0]), "=&r" ((p)[1]), "=c" ((p)[2]), "=d" ((p)[3]) : "0" (func), "2"(0)); }
/* "=&r" selects free register. It can select even rbx, if that register is free.
"=&D" for (RDI) also works, but the code can be larger with "=&D"
"2"(0) means (subFunction = 0),
2 is (zero-based) index in the output constraint list "=c" (ECX). */
: "=a" ((p)[0]), "=&r" ((p)[1]), "=c" ((p)[2]), "=d" ((p)[3]) : "0" (func), "2"(subFunc)); }
#elif defined(MY_CPU_X86) && defined(__PIC__) \
&& ((defined (__GNUC__) && (__GNUC__ < 5)) || defined(__clang__))
#define x86_cpuid_MACRO(p, func) { \
#define x86_cpuid_MACRO_2(p, func, subFunc) { \
__asm__ __volatile__ ( \
ASM_LN "mov %%ebx, %k1" \
ASM_LN "cpuid" \
ASM_LN "xchg %%ebx, %k1" \
: "=a" ((p)[0]), "=&r" ((p)[1]), "=c" ((p)[2]), "=d" ((p)[3]) : "0" (func), "2"(0)); }
: "=a" ((p)[0]), "=&r" ((p)[1]), "=c" ((p)[2]), "=d" ((p)[3]) : "0" (func), "2"(subFunc)); }
#else
#define x86_cpuid_MACRO(p, func) { \
#define x86_cpuid_MACRO_2(p, func, subFunc) { \
__asm__ __volatile__ ( \
ASM_LN "cpuid" \
: "=a" ((p)[0]), "=b" ((p)[1]), "=c" ((p)[2]), "=d" ((p)[3]) : "0" (func), "2"(0)); }
: "=a" ((p)[0]), "=b" ((p)[1]), "=c" ((p)[2]), "=d" ((p)[3]) : "0" (func), "2"(subFunc)); }
#endif
#define x86_cpuid_MACRO(p, func) x86_cpuid_MACRO_2(p, func, 0)
void Z7_FASTCALL z7_x86_cpuid(UInt32 p[4], UInt32 func)
{
x86_cpuid_MACRO(p, func)
}
static
void Z7_FASTCALL z7_x86_cpuid_subFunc(UInt32 p[4], UInt32 func, UInt32 subFunc)
{
x86_cpuid_MACRO_2(p, func, subFunc)
}
Z7_NO_INLINE
UInt32 Z7_FASTCALL z7_x86_cpuid_GetMaxFunc(void)
@@ -205,11 +211,39 @@ void __declspec(naked) Z7_FASTCALL z7_x86_cpuid(UInt32 p[4], UInt32 func)
__asm ret 0
}
static
void __declspec(naked) Z7_FASTCALL z7_x86_cpuid_subFunc(UInt32 p[4], UInt32 func, UInt32 subFunc)
{
UNUSED_VAR(p)
UNUSED_VAR(func)
UNUSED_VAR(subFunc)
__asm push ebx
__asm push edi
__asm mov edi, ecx // p
__asm mov eax, edx // func
__asm mov ecx, [esp + 12] // subFunc
__asm cpuid
__asm mov [edi ], eax
__asm mov [edi + 4], ebx
__asm mov [edi + 8], ecx
__asm mov [edi + 12], edx
__asm pop edi
__asm pop ebx
__asm ret 4
}
#else // MY_CPU_AMD64
#if _MSC_VER >= 1600
#include <intrin.h>
#define MY_cpuidex __cpuidex
static
void Z7_FASTCALL z7_x86_cpuid_subFunc(UInt32 p[4], UInt32 func, UInt32 subFunc)
{
__cpuidex((int *)p, func, subFunc);
}
#else
/*
__cpuid (func == (0 or 7)) requires subfunction number in ECX.
@@ -219,7 +253,7 @@ void __declspec(naked) Z7_FASTCALL z7_x86_cpuid(UInt32 p[4], UInt32 func)
We still can use __cpuid for low (func) values that don't require ECX,
but __cpuid() in old MSVC will be incorrect for some func values: (func == 7).
So here we use the hack for old MSVC to send (subFunction) in ECX register to cpuid instruction,
where ECX value is first parameter for FASTCALL / NO_INLINE func,
where ECX value is first parameter for FASTCALL / NO_INLINE func.
So the caller of MY_cpuidex_HACK() sets ECX as subFunction, and
old MSVC for __cpuid() doesn't change ECX and cpuid instruction gets (subFunction) value.
@@ -233,6 +267,11 @@ Z7_NO_INLINE void Z7_FASTCALL MY_cpuidex_HACK(Int32 subFunction, Int32 func, Int
}
#define MY_cpuidex(info, func, func2) MY_cpuidex_HACK(func2, func, info)
#pragma message("======== MY_cpuidex_HACK WAS USED ========")
static
void Z7_FASTCALL z7_x86_cpuid_subFunc(UInt32 p[4], UInt32 func, UInt32 subFunc)
{
MY_cpuidex_HACK(subFunc, func, (Int32 *)p);
}
#endif // _MSC_VER >= 1600
#if !defined(MY_CPU_AMD64)
@@ -445,6 +484,23 @@ BoolInt CPU_IsSupported_SHA(void)
}
}
BoolInt CPU_IsSupported_SHA512(void)
{
if (!CPU_IsSupported_AVX2()) return False; // maybe CPU_IsSupported_AVX() is enough here
if (z7_x86_cpuid_GetMaxFunc() < 7)
return False;
{
UInt32 d[4];
z7_x86_cpuid_subFunc(d, 7, 0);
if (d[0] < 1) // d[0] - is max supported subleaf value
return False;
z7_x86_cpuid_subFunc(d, 7, 1);
return (BoolInt)(d[0]) & 1;
}
}
/*
MSVC: _xgetbv() intrinsic is available since VS2010SP1.
MSVC also defines (_XCR_XFEATURE_ENABLED_MASK) macro in
@@ -776,6 +832,18 @@ BoolInt CPU_IsSupported_NEON(void)
return z7_sysctlbyname_Get_BoolInt("hw.optional.neon");
}
BoolInt CPU_IsSupported_SHA512(void)
{
return z7_sysctlbyname_Get_BoolInt("hw.optional.armv8_2_sha512");
}
/*
BoolInt CPU_IsSupported_SHA3(void)
{
return z7_sysctlbyname_Get_BoolInt("hw.optional.armv8_2_sha3");
}
*/
#ifdef MY_CPU_ARM64
#define APPLE_CRYPTO_SUPPORT_VAL 1
#else
@@ -860,6 +928,19 @@ MY_HWCAP_CHECK_FUNC (CRC32)
MY_HWCAP_CHECK_FUNC (SHA1)
MY_HWCAP_CHECK_FUNC (SHA2)
MY_HWCAP_CHECK_FUNC (AES)
#ifdef MY_CPU_ARM64
// <hwcap.h> supports HWCAP_SHA512 and HWCAP_SHA3 since 2017.
// we define them here, if they are not defined
#ifndef HWCAP_SHA3
// #define HWCAP_SHA3 (1 << 17)
#endif
#ifndef HWCAP_SHA512
// #pragma message("=== HWCAP_SHA512 define === ")
#define HWCAP_SHA512 (1 << 21)
#endif
MY_HWCAP_CHECK_FUNC (SHA512)
// MY_HWCAP_CHECK_FUNC (SHA3)
#endif
#endif // __APPLE__
#endif // _WIN32

View File

@@ -1,5 +1,5 @@
/* LzFind.c -- Match finder for LZ algorithms
2024-03-01 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -404,7 +404,7 @@ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
const unsigned nbMax =
(p->numHashBytes == 2 ? 16 :
(p->numHashBytes == 3 ? 24 : 32));
if (numBits > nbMax)
if (numBits >= nbMax)
numBits = nbMax;
if (numBits >= 32)
hs = (UInt32)0 - 1;
@@ -416,14 +416,14 @@ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
hs |= (256 << kLzHash_CrcShift_2) - 1;
{
const UInt32 hs2 = MatchFinder_GetHashMask2(p, historySize);
if (hs > hs2)
if (hs >= hs2)
hs = hs2;
}
hsCur = hs;
if (p->expectedDataSize < historySize)
{
const UInt32 hs2 = MatchFinder_GetHashMask2(p, (UInt32)p->expectedDataSize);
if (hsCur > hs2)
if (hsCur >= hs2)
hsCur = hs2;
}
}
@@ -434,7 +434,7 @@ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
if (p->expectedDataSize < historySize)
{
hsCur = MatchFinder_GetHashMask(p, (UInt32)p->expectedDataSize);
if (hsCur > hs) // is it possible?
if (hsCur >= hs) // is it possible?
hsCur = hs;
}
}
@@ -890,7 +890,7 @@ static UInt32 * Hc_GetMatchesSpec(size_t lenLimit, UInt32 curMatch, UInt32 pos,
return d;
{
const Byte *pb = cur - delta;
curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
curMatch = son[_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)];
if (pb[maxLen] == cur[maxLen] && *pb == *cur)
{
UInt32 len = 0;
@@ -925,7 +925,7 @@ static UInt32 * Hc_GetMatchesSpec(size_t lenLimit, UInt32 curMatch, UInt32 pos,
break;
{
ptrdiff_t diff;
curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
curMatch = son[_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)];
diff = (ptrdiff_t)0 - (ptrdiff_t)delta;
if (cur[maxLen] == cur[(ptrdiff_t)maxLen + diff])
{
@@ -972,7 +972,7 @@ UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byt
// if (curMatch >= pos) { *ptr0 = *ptr1 = kEmptyHashValue; return NULL; }
cmCheck = (UInt32)(pos - _cyclicBufferSize);
if ((UInt32)pos <= _cyclicBufferSize)
if ((UInt32)pos < _cyclicBufferSize)
cmCheck = 0;
if (cmCheck < curMatch)
@@ -980,7 +980,7 @@ UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byt
{
const UInt32 delta = pos - curMatch;
{
CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)) << 1);
const Byte *pb = cur - delta;
unsigned len = (len0 < len1 ? len0 : len1);
const UInt32 pair0 = pair[0];
@@ -1039,7 +1039,7 @@ static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const
UInt32 cmCheck;
cmCheck = (UInt32)(pos - _cyclicBufferSize);
if ((UInt32)pos <= _cyclicBufferSize)
if ((UInt32)pos < _cyclicBufferSize)
cmCheck = 0;
if (// curMatch >= pos || // failure
@@ -1048,7 +1048,7 @@ static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const
{
const UInt32 delta = pos - curMatch;
{
CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)) << 1);
const Byte *pb = cur - delta;
unsigned len = (len0 < len1 ? len0 : len1);
if (pb[len] == cur[len])
@@ -1595,7 +1595,7 @@ static void Bt5_MatchFinder_Skip(void *_p, UInt32 num)
UInt32 pos = p->pos; \
UInt32 num2 = num; \
/* (p->pos == p->posLimit) is not allowed here !!! */ \
{ const UInt32 rem = p->posLimit - pos; if (num2 > rem) num2 = rem; } \
{ const UInt32 rem = p->posLimit - pos; if (num2 >= rem) num2 = rem; } \
num -= num2; \
{ const UInt32 cycPos = p->cyclicBufferPos; \
son = p->son + cycPos; \

View File

@@ -1,5 +1,5 @@
/* LzFindMt.c -- multithreaded Match finder for LZ algorithms
2024-01-22 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -82,6 +82,8 @@ extern UInt64 g_NumIters_Bytes;
Z7_NO_INLINE
static void MtSync_Construct(CMtSync *p)
{
p->affinityGroup = -1;
p->affinityInGroup = 0;
p->affinity = 0;
p->wasCreated = False;
p->csWasInitialized = False;
@@ -259,6 +261,12 @@ static WRes MtSync_Create_WRes(CMtSync *p, THREAD_FUNC_TYPE startAddress, void *
// return ERROR_TOO_MANY_POSTS; // for debug
// return EINVAL; // for debug
#ifdef _WIN32
if (p->affinityGroup >= 0)
wres = Thread_Create_With_Group(&p->thread, startAddress, obj,
(unsigned)(UInt32)p->affinityGroup, (CAffinityMask)p->affinityInGroup);
else
#endif
if (p->affinity != 0)
wres = Thread_Create_With_Affinity(&p->thread, startAddress, obj, (CAffinityMask)p->affinity);
else

View File

@@ -1,5 +1,5 @@
/* Lzma2Enc.c -- LZMA2 Encoder
2023-04-13 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -235,6 +235,7 @@ void Lzma2EncProps_Init(CLzma2EncProps *p)
p->numBlockThreads_Reduced = -1;
p->numBlockThreads_Max = -1;
p->numTotalThreads = -1;
p->numThreadGroups = 0;
}
void Lzma2EncProps_Normalize(CLzma2EncProps *p)
@@ -781,6 +782,7 @@ SRes Lzma2Enc_Encode2(CLzma2EncHandle p,
}
p->mtCoder.numThreadsMax = (unsigned)p->props.numBlockThreads_Max;
p->mtCoder.numThreadGroups = p->props.numThreadGroups;
p->mtCoder.expectedDataSize = p->expectedDataSize;
{

View File

@@ -1,5 +1,5 @@
/* LzmaEnc.c -- LZMA Encoder
2024-01-24: Igor Pavlov : Public domain */
Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -62,7 +62,9 @@ void LzmaEncProps_Init(CLzmaEncProps *p)
p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1;
p->numHashOutBits = 0;
p->writeEndMark = 0;
p->affinityGroup = -1;
p->affinity = 0;
p->affinityInGroup = 0;
}
void LzmaEncProps_Normalize(CLzmaEncProps *p)
@@ -72,11 +74,11 @@ void LzmaEncProps_Normalize(CLzmaEncProps *p)
p->level = level;
if (p->dictSize == 0)
p->dictSize =
( level <= 3 ? ((UInt32)1 << (level * 2 + 16)) :
( level <= 6 ? ((UInt32)1 << (level + 19)) :
( level <= 7 ? ((UInt32)1 << 25) : ((UInt32)1 << 26)
)));
p->dictSize = (unsigned)level <= 4 ?
(UInt32)1 << (level * 2 + 16) :
(unsigned)level <= sizeof(size_t) / 2 + 4 ?
(UInt32)1 << (level + 20) :
(UInt32)1 << (sizeof(size_t) / 2 + 24);
if (p->dictSize > p->reduceSize)
{
@@ -92,8 +94,8 @@ void LzmaEncProps_Normalize(CLzmaEncProps *p)
if (p->lp < 0) p->lp = 0;
if (p->pb < 0) p->pb = 2;
if (p->algo < 0) p->algo = (level < 5 ? 0 : 1);
if (p->fb < 0) p->fb = (level < 7 ? 32 : 64);
if (p->algo < 0) p->algo = (unsigned)level < 5 ? 0 : 1;
if (p->fb < 0) p->fb = (unsigned)level < 7 ? 32 : 64;
if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1);
if (p->numHashBytes < 0) p->numHashBytes = (p->btMode ? 4 : 5);
if (p->mc == 0) p->mc = (16 + ((unsigned)p->fb >> 1)) >> (p->btMode ? 0 : 1);
@@ -598,6 +600,10 @@ SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props2)
p->multiThread = (props.numThreads > 1);
p->matchFinderMt.btSync.affinity =
p->matchFinderMt.hashSync.affinity = props.affinity;
p->matchFinderMt.btSync.affinityGroup =
p->matchFinderMt.hashSync.affinityGroup = props.affinityGroup;
p->matchFinderMt.btSync.affinityInGroup =
p->matchFinderMt.hashSync.affinityInGroup = props.affinityInGroup;
#endif
return SZ_OK;

View File

@@ -1,5 +1,5 @@
/* MtCoder.c -- Multi-thread Coder
2023-09-07 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -39,14 +39,28 @@ void MtProgressThunk_CreateVTable(CMtProgressThunk *p)
static THREAD_FUNC_DECL ThreadFunc(void *pp);
static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t)
static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t
#ifdef _WIN32
, CMtCoder * const mtc
#endif
)
{
WRes wres = AutoResetEvent_OptCreate_And_Reset(&t->startEvent);
// printf("\n====== MtCoderThread_CreateAndStart : \n");
if (wres == 0)
{
t->stop = False;
if (!Thread_WasCreated(&t->thread))
wres = Thread_Create(&t->thread, ThreadFunc, t);
{
#ifdef _WIN32
if (mtc->numThreadGroups)
wres = Thread_Create_With_Group(&t->thread, ThreadFunc, t,
ThreadNextGroup_GetNext(&mtc->nextGroup), // group
0); // affinityMask
else
#endif
wres = Thread_Create(&t->thread, ThreadFunc, t);
}
if (wres == 0)
wres = Event_Set(&t->startEvent);
}
@@ -56,6 +70,7 @@ static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t)
}
Z7_FORCE_INLINE
static void MtCoderThread_Destruct(CMtCoderThread *t)
{
if (Thread_WasCreated(&t->thread))
@@ -85,7 +100,7 @@ static void MtCoderThread_Destruct(CMtCoderThread *t)
static SRes ThreadFunc2(CMtCoderThread *t)
{
CMtCoder *mtc = t->mtCoder;
CMtCoder * const mtc = t->mtCoder;
for (;;)
{
@@ -185,7 +200,11 @@ static SRes ThreadFunc2(CMtCoderThread *t)
if (mtc->numStartedThreads < mtc->numStartedThreadsLimit
&& mtc->expectedDataSize != readProcessed)
{
res = MtCoderThread_CreateAndStart(&mtc->threads[mtc->numStartedThreads]);
res = MtCoderThread_CreateAndStart(&mtc->threads[mtc->numStartedThreads]
#ifdef _WIN32
, mtc
#endif
);
if (res == SZ_OK)
mtc->numStartedThreads++;
else
@@ -221,7 +240,7 @@ static SRes ThreadFunc2(CMtCoderThread *t)
}
{
CMtCoderBlock *block = &mtc->blocks[bi];
CMtCoderBlock * const block = &mtc->blocks[bi];
block->res = res;
block->bufIndex = bufIndex;
block->finished = finished;
@@ -311,7 +330,7 @@ static SRes ThreadFunc2(CMtCoderThread *t)
static THREAD_FUNC_DECL ThreadFunc(void *pp)
{
CMtCoderThread *t = (CMtCoderThread *)pp;
CMtCoderThread * const t = (CMtCoderThread *)pp;
for (;;)
{
if (Event_Wait(&t->startEvent) != 0)
@@ -319,7 +338,7 @@ static THREAD_FUNC_DECL ThreadFunc(void *pp)
if (t->stop)
return 0;
{
SRes res = ThreadFunc2(t);
const SRes res = ThreadFunc2(t);
CMtCoder *mtc = t->mtCoder;
if (res != SZ_OK)
{
@@ -328,7 +347,7 @@ static THREAD_FUNC_DECL ThreadFunc(void *pp)
#ifndef MTCODER_USE_WRITE_THREAD
{
unsigned numFinished = (unsigned)InterlockedIncrement(&mtc->numFinishedThreads);
const unsigned numFinished = (unsigned)InterlockedIncrement(&mtc->numFinishedThreads);
if (numFinished == mtc->numStartedThreads)
if (Event_Set(&mtc->finishedEvent) != 0)
return (THREAD_FUNC_RET_TYPE)SZ_ERROR_THREAD;
@@ -346,6 +365,7 @@ void MtCoder_Construct(CMtCoder *p)
p->blockSize = 0;
p->numThreadsMax = 0;
p->numThreadGroups = 0;
p->expectedDataSize = (UInt64)(Int64)-1;
p->inStream = NULL;
@@ -429,6 +449,8 @@ SRes MtCoder_Code(CMtCoder *p)
unsigned i;
SRes res = SZ_OK;
// printf("\n====== MtCoder_Code : \n");
if (numThreads > MTCODER_THREADS_MAX)
numThreads = MTCODER_THREADS_MAX;
numBlocksMax = MTCODER_GET_NUM_BLOCKS_FROM_THREADS(numThreads);
@@ -492,11 +514,22 @@ SRes MtCoder_Code(CMtCoder *p)
p->numStartedThreadsLimit = numThreads;
p->numStartedThreads = 0;
ThreadNextGroup_Init(&p->nextGroup, p->numThreadGroups, 0); // startGroup
// for (i = 0; i < numThreads; i++)
{
// here we create new thread for first block.
// And each new thread will create another new thread after block reading
// until numStartedThreadsLimit is reached.
CMtCoderThread *nextThread = &p->threads[p->numStartedThreads++];
RINOK(MtCoderThread_CreateAndStart(nextThread))
{
const SRes res2 = MtCoderThread_CreateAndStart(nextThread
#ifdef _WIN32
, p
#endif
);
RINOK(res2)
}
}
RINOK_THREAD(Event_Set(&p->readEvent))
@@ -513,9 +546,9 @@ SRes MtCoder_Code(CMtCoder *p)
RINOK_THREAD(Event_Wait(&p->writeEvents[bi]))
{
const CMtCoderBlock *block = &p->blocks[bi];
unsigned bufIndex = block->bufIndex;
BoolInt finished = block->finished;
const CMtCoderBlock * const block = &p->blocks[bi];
const unsigned bufIndex = block->bufIndex;
const BoolInt finished = block->finished;
if (res == SZ_OK && block->res != SZ_OK)
res = block->res;
@@ -545,7 +578,7 @@ SRes MtCoder_Code(CMtCoder *p)
}
#else
{
WRes wres = Event_Wait(&p->finishedEvent);
const WRes wres = Event_Wait(&p->finishedEvent);
res = MY_SRes_HRESULT_FROM_WRes(wres);
}
#endif

View File

@@ -1,18 +1,14 @@
/* Sha256.c -- SHA-256 Hash
2024-03-01 : Igor Pavlov : Public domain
: Igor Pavlov : Public domain
This code is based on public domain code from Wei Dai's Crypto++ library. */
#include "Precomp.h"
#include <string.h>
#include "CpuArch.h"
#include "RotateDefs.h"
#include "Sha256.h"
#if defined(_MSC_VER) && (_MSC_VER < 1900)
// #define USE_MY_MM
#endif
#include "RotateDefs.h"
#include "CpuArch.h"
#ifdef MY_CPU_X86_OR_AMD64
#if defined(Z7_LLVM_CLANG_VERSION) && (Z7_LLVM_CLANG_VERSION >= 30800) \
@@ -56,7 +52,7 @@ void Z7_FASTCALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t n
static SHA256_FUNC_UPDATE_BLOCKS g_SHA256_FUNC_UPDATE_BLOCKS = Sha256_UpdateBlocks;
static SHA256_FUNC_UPDATE_BLOCKS g_SHA256_FUNC_UPDATE_BLOCKS_HW;
#define SHA256_UPDATE_BLOCKS(p) p->func_UpdateBlocks
#define SHA256_UPDATE_BLOCKS(p) p->v.vars.func_UpdateBlocks
#else
#define SHA256_UPDATE_BLOCKS(p) Sha256_UpdateBlocks
#endif
@@ -85,7 +81,7 @@ BoolInt Sha256_SetFunction(CSha256 *p, unsigned algo)
return False;
#endif
p->func_UpdateBlocks = func;
p->v.vars.func_UpdateBlocks = func;
return True;
}
@@ -111,7 +107,7 @@ BoolInt Sha256_SetFunction(CSha256 *p, unsigned algo)
void Sha256_InitState(CSha256 *p)
{
p->count = 0;
p->v.vars.count = 0;
p->state[0] = 0x6a09e667;
p->state[1] = 0xbb67ae85;
p->state[2] = 0x3c6ef372;
@@ -122,9 +118,16 @@ void Sha256_InitState(CSha256 *p)
p->state[7] = 0x5be0cd19;
}
void Sha256_Init(CSha256 *p)
{
p->func_UpdateBlocks =
p->v.vars.func_UpdateBlocks =
#ifdef Z7_COMPILER_SHA256_SUPPORTED
g_SHA256_FUNC_UPDATE_BLOCKS;
#else
@@ -133,10 +136,10 @@ void Sha256_Init(CSha256 *p)
Sha256_InitState(p);
}
#define S0(x) (rotrFixed(x, 2) ^ rotrFixed(x,13) ^ rotrFixed(x, 22))
#define S1(x) (rotrFixed(x, 6) ^ rotrFixed(x,11) ^ rotrFixed(x, 25))
#define S0(x) (rotrFixed(x, 2) ^ rotrFixed(x,13) ^ rotrFixed(x,22))
#define S1(x) (rotrFixed(x, 6) ^ rotrFixed(x,11) ^ rotrFixed(x,25))
#define s0(x) (rotrFixed(x, 7) ^ rotrFixed(x,18) ^ (x >> 3))
#define s1(x) (rotrFixed(x,17) ^ rotrFixed(x,19) ^ (x >> 10))
#define s1(x) (rotrFixed(x,17) ^ rotrFixed(x,19) ^ (x >>10))
#define Ch(x,y,z) (z^(x&(y^z)))
#define Maj(x,y,z) ((x&y)|(z&(x|y)))
@@ -224,12 +227,10 @@ void Sha256_Init(CSha256 *p)
#endif
// static
extern MY_ALIGN(64)
const UInt32 SHA256_K_ARRAY[64];
MY_ALIGN(64)
const UInt32 SHA256_K_ARRAY[64] = {
extern
MY_ALIGN(64) const UInt32 SHA256_K_ARRAY[64];
MY_ALIGN(64) const UInt32 SHA256_K_ARRAY[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
@@ -248,27 +249,29 @@ const UInt32 SHA256_K_ARRAY[64] = {
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
#define K SHA256_K_ARRAY
#define K SHA256_K_ARRAY
Z7_NO_INLINE
void Z7_FASTCALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t numBlocks)
{
UInt32 W
#ifdef Z7_SHA256_BIG_W
#ifdef Z7_SHA256_BIG_W
[64];
#else
#else
[16];
#endif
#endif
unsigned j;
UInt32 a,b,c,d,e,f,g,h;
#if !defined(Z7_SHA256_UNROLL) || (STEP_MAIN <= 4) || (STEP_PRE <= 4)
#if !defined(Z7_SHA256_UNROLL) || (STEP_MAIN <= 4) || (STEP_PRE <= 4)
UInt32 tmp;
#endif
#endif
if (numBlocks == 0) return;
a = state[0];
b = state[1];
c = state[2];
@@ -278,7 +281,7 @@ void Z7_FASTCALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t n
g = state[6];
h = state[7];
while (numBlocks)
do
{
for (j = 0; j < 16; j += STEP_PRE)
@@ -352,19 +355,11 @@ void Z7_FASTCALL Sha256_UpdateBlocks(UInt32 state[8], const Byte *data, size_t n
g += state[6]; state[6] = g;
h += state[7]; state[7] = h;
data += 64;
numBlocks--;
data += SHA256_BLOCK_SIZE;
}
/* Wipe variables */
/* memset(W, 0, sizeof(W)); */
while (--numBlocks);
}
#undef S0
#undef S1
#undef s0
#undef s1
#undef K
#define Sha256_UpdateBlock(p) SHA256_UPDATE_BLOCKS(p)(p->state, p->buffer, 1)
@@ -372,20 +367,15 @@ void Sha256_Update(CSha256 *p, const Byte *data, size_t size)
{
if (size == 0)
return;
{
unsigned pos = (unsigned)p->count & 0x3F;
unsigned num;
p->count += size;
num = 64 - pos;
const unsigned pos = (unsigned)p->v.vars.count & (SHA256_BLOCK_SIZE - 1);
const unsigned num = SHA256_BLOCK_SIZE - pos;
p->v.vars.count += size;
if (num > size)
{
memcpy(p->buffer + pos, data, size);
return;
}
if (pos != 0)
{
size -= num;
@@ -395,9 +385,10 @@ void Sha256_Update(CSha256 *p, const Byte *data, size_t size)
}
}
{
size_t numBlocks = size >> 6;
const size_t numBlocks = size >> 6;
// if (numBlocks)
SHA256_UPDATE_BLOCKS(p)(p->state, data, numBlocks);
size &= 0x3F;
size &= SHA256_BLOCK_SIZE - 1;
if (size == 0)
return;
data += (numBlocks << 6);
@@ -408,82 +399,69 @@ void Sha256_Update(CSha256 *p, const Byte *data, size_t size)
void Sha256_Final(CSha256 *p, Byte *digest)
{
unsigned pos = (unsigned)p->count & 0x3F;
unsigned i;
unsigned pos = (unsigned)p->v.vars.count & (SHA256_BLOCK_SIZE - 1);
p->buffer[pos++] = 0x80;
if (pos > (64 - 8))
if (pos > (SHA256_BLOCK_SIZE - 4 * 2))
{
while (pos != 64) { p->buffer[pos++] = 0; }
// memset(&p->buf.buffer[pos], 0, 64 - pos);
while (pos != SHA256_BLOCK_SIZE) { p->buffer[pos++] = 0; }
// memset(&p->buf.buffer[pos], 0, SHA256_BLOCK_SIZE - pos);
Sha256_UpdateBlock(p);
pos = 0;
}
/*
if (pos & 3)
memset(&p->buffer[pos], 0, (SHA256_BLOCK_SIZE - 4 * 2) - pos);
{
p->buffer[pos] = 0;
p->buffer[pos + 1] = 0;
p->buffer[pos + 2] = 0;
pos += 3;
pos &= ~3;
const UInt64 numBits = p->v.vars.count << 3;
SetBe32(p->buffer + SHA256_BLOCK_SIZE - 4 * 2, (UInt32)(numBits >> 32))
SetBe32(p->buffer + SHA256_BLOCK_SIZE - 4 * 1, (UInt32)(numBits))
}
{
for (; pos < 64 - 8; pos += 4)
*(UInt32 *)(&p->buffer[pos]) = 0;
}
*/
memset(&p->buffer[pos], 0, (64 - 8) - pos);
{
UInt64 numBits = (p->count << 3);
SetBe32(p->buffer + 64 - 8, (UInt32)(numBits >> 32))
SetBe32(p->buffer + 64 - 4, (UInt32)(numBits))
}
Sha256_UpdateBlock(p);
for (i = 0; i < 8; i += 2)
#if 1 && defined(MY_CPU_BE)
memcpy(digest, p->state, SHA256_DIGEST_SIZE);
#else
{
UInt32 v0 = p->state[i];
UInt32 v1 = p->state[(size_t)i + 1];
SetBe32(digest , v0)
SetBe32(digest + 4, v1)
digest += 8;
unsigned i;
for (i = 0; i < 8; i += 2)
{
const UInt32 v0 = p->state[i];
const UInt32 v1 = p->state[(size_t)i + 1];
SetBe32(digest , v0)
SetBe32(digest + 4, v1)
digest += 4 * 2;
}
}
#endif
Sha256_InitState(p);
}
void Sha256Prepare(void)
{
#ifdef Z7_COMPILER_SHA256_SUPPORTED
#ifdef Z7_COMPILER_SHA256_SUPPORTED
SHA256_FUNC_UPDATE_BLOCKS f, f_hw;
f = Sha256_UpdateBlocks;
f_hw = NULL;
#ifdef MY_CPU_X86_OR_AMD64
#ifndef USE_MY_MM
#ifdef MY_CPU_X86_OR_AMD64
if (CPU_IsSupported_SHA()
&& CPU_IsSupported_SSSE3()
// && CPU_IsSupported_SSE41()
)
#endif
#else
#else
if (CPU_IsSupported_SHA2())
#endif
#endif
{
// printf("\n========== HW SHA256 ======== \n");
f = f_hw = Sha256_UpdateBlocks_HW;
}
g_SHA256_FUNC_UPDATE_BLOCKS = f;
g_SHA256_FUNC_UPDATE_BLOCKS_HW = f_hw;
#endif
#endif
}
#undef U64C
#undef K
#undef S0
#undef S1
#undef s0

View File

@@ -1,18 +1,11 @@
/* Sha256Opt.c -- SHA-256 optimized code for SHA-256 hardware instructions
2024-03-01 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
#include "Compiler.h"
#include "CpuArch.h"
#if defined(_MSC_VER)
#if (_MSC_VER < 1900) && (_MSC_VER >= 1200)
// #define USE_MY_MM
#endif
#endif
// #define Z7_USE_HW_SHA_STUB // for debug
#ifdef MY_CPU_X86_OR_AMD64
#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1600) // fix that check
#define USE_HW_SHA
@@ -20,19 +13,14 @@
|| defined(Z7_APPLE_CLANG_VERSION) && (Z7_APPLE_CLANG_VERSION >= 50100) \
|| defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION >= 40900)
#define USE_HW_SHA
#if !defined(_INTEL_COMPILER)
#if !defined(__INTEL_COMPILER)
// icc defines __GNUC__, but icc doesn't support __attribute__(__target__)
#if !defined(__SHA__) || !defined(__SSSE3__)
#define ATTRIB_SHA __attribute__((__target__("sha,ssse3")))
#endif
#endif
#elif defined(_MSC_VER)
#ifdef USE_MY_MM
#define USE_VER_MIN 1300
#else
#define USE_VER_MIN 1900
#endif
#if (_MSC_VER >= USE_VER_MIN)
#if (_MSC_VER >= 1900)
#define USE_HW_SHA
#else
#define Z7_USE_HW_SHA_STUB
@@ -47,23 +35,20 @@
// #pragma message("Sha256 HW")
// sse/sse2/ssse3:
#include <tmmintrin.h>
// sha*:
#include <immintrin.h>
#if defined (__clang__) && defined(_MSC_VER)
// #if !defined(__SSSE3__)
// #endif
#if !defined(__SHA__)
#include <shaintrin.h>
#endif
#else
#ifdef USE_MY_MM
#include "My_mm.h"
#endif
#endif
/*
@@ -91,60 +76,44 @@ SHA:
extern
MY_ALIGN(64)
const UInt32 SHA256_K_ARRAY[64];
#define K SHA256_K_ARRAY
#define ADD_EPI32(dest, src) dest = _mm_add_epi32(dest, src);
#define SHA256_MSG1(dest, src) dest = _mm_sha256msg1_epu32(dest, src);
#define SHA25G_MSG2(dest, src) dest = _mm_sha256msg2_epu32(dest, src);
#define SHA256_MSG2(dest, src) dest = _mm_sha256msg2_epu32(dest, src);
#define LOAD_SHUFFLE(m, k) \
m = _mm_loadu_si128((const __m128i *)(const void *)(data + (k) * 16)); \
m = _mm_shuffle_epi8(m, mask); \
#define SM1(g0, g1, g2, g3) \
SHA256_MSG1(g3, g0); \
#define NNN(m0, m1, m2, m3)
#define SM2(g0, g1, g2, g3) \
tmp = _mm_alignr_epi8(g1, g0, 4); \
ADD_EPI32(g2, tmp) \
SHA25G_MSG2(g2, g1); \
// #define LS0(k, g0, g1, g2, g3) LOAD_SHUFFLE(g0, k)
// #define LS1(k, g0, g1, g2, g3) LOAD_SHUFFLE(g1, k+1)
#define NNN(g0, g1, g2, g3)
#define SM1(m1, m2, m3, m0) \
SHA256_MSG1(m0, m1); \
#define SM2(m2, m3, m0, m1) \
ADD_EPI32(m0, _mm_alignr_epi8(m3, m2, 4)) \
SHA256_MSG2(m0, m3); \
#define RND2(t0, t1) \
t0 = _mm_sha256rnds2_epu32(t0, t1, msg);
#define RND2_0(m, k) \
msg = _mm_add_epi32(m, *(const __m128i *) (const void *) &K[(k) * 4]); \
#define R4(k, m0, m1, m2, m3, OP0, OP1) \
msg = _mm_add_epi32(m0, *(const __m128i *) (const void *) &K[(k) * 4]); \
RND2(state0, state1); \
msg = _mm_shuffle_epi32(msg, 0x0E); \
#define RND2_1 \
OP0(m0, m1, m2, m3) \
RND2(state1, state0); \
// We use scheme with 3 rounds ahead for SHA256_MSG1 / 2 rounds ahead for SHA256_MSG2
#define R4(k, g0, g1, g2, g3, OP0, OP1) \
RND2_0(g0, k) \
OP0(g0, g1, g2, g3) \
RND2_1 \
OP1(g0, g1, g2, g3) \
OP1(m0, m1, m2, m3) \
#define R16(k, OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7) \
R4 ( (k)*4+0, m0,m1,m2,m3, OP0, OP1 ) \
R4 ( (k)*4+1, m1,m2,m3,m0, OP2, OP3 ) \
R4 ( (k)*4+2, m2,m3,m0,m1, OP4, OP5 ) \
R4 ( (k)*4+3, m3,m0,m1,m2, OP6, OP7 ) \
R4 ( (k)*4+0, m0,m1,m2,m3, OP0, OP1 ) \
R4 ( (k)*4+1, m1,m2,m3,m0, OP2, OP3 ) \
R4 ( (k)*4+2, m2,m3,m0,m1, OP4, OP5 ) \
R4 ( (k)*4+3, m3,m0,m1,m2, OP6, OP7 ) \
#define PREPARE_STATE \
tmp = _mm_shuffle_epi32(state0, 0x1B); /* abcd */ \
@@ -161,8 +130,9 @@ ATTRIB_SHA
void Z7_FASTCALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_t numBlocks)
{
const __m128i mask = _mm_set_epi32(0x0c0d0e0f, 0x08090a0b, 0x04050607, 0x00010203);
__m128i tmp;
__m128i state0, state1;
__m128i tmp, state0, state1;
if (numBlocks == 0)
return;
@@ -262,22 +232,10 @@ void Z7_FASTCALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_
#define _ARM_USE_NEW_NEON_INTRINSICS
#endif
#if defined(Z7_MSC_VER_ORIGINAL) && defined(MY_CPU_ARM64)
#include <arm64_neon.h>
#else
#if defined(__clang__) && __clang_major__ < 16
#if !defined(__ARM_FEATURE_SHA2) && \
!defined(__ARM_FEATURE_CRYPTO)
@@ -324,41 +282,70 @@ typedef uint32x4_t v128;
// typedef __n128 v128; // MSVC
#ifdef MY_CPU_BE
#define MY_rev32_for_LE(x)
#define MY_rev32_for_LE(x) x
#else
#define MY_rev32_for_LE(x) x = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(x)))
#define MY_rev32_for_LE(x) vrev32q_u8(x)
#endif
#define LOAD_128(_p) (*(const v128 *)(const void *)(_p))
#define STORE_128(_p, _v) *(v128 *)(void *)(_p) = (_v)
#if 1 // 0 for debug
// for arm32: it works slower by some reason than direct code
/*
for arm32 it generates:
MSVC-2022, GCC-9:
vld1.32 {d18,d19}, [r10]
vst1.32 {d4,d5}, [r3]
vld1.8 {d20-d21}, [r4]
there is no align hint (like [r10:128]). So instruction allows unaligned access
*/
#define LOAD_128_32(_p) vld1q_u32(_p)
#define LOAD_128_8(_p) vld1q_u8 (_p)
#define STORE_128_32(_p, _v) vst1q_u32(_p, _v)
#else
/*
for arm32:
MSVC-2022:
vldm r10,{d18,d19}
vstm r3,{d4,d5}
does it require strict alignment?
GCC-9:
vld1.64 {d30-d31}, [r0:64]
vldr d28, [r0, #16]
vldr d29, [r0, #24]
vst1.64 {d30-d31}, [r0:64]
vstr d28, [r0, #16]
vstr d29, [r0, #24]
there is hint [r0:64], so does it requires 64-bit alignment.
*/
#define LOAD_128_32(_p) (*(const v128 *)(const void *)(_p))
#define LOAD_128_8(_p) vreinterpretq_u8_u32(*(const v128 *)(const void *)(_p))
#define STORE_128_32(_p, _v) *(v128 *)(void *)(_p) = (_v)
#endif
#define LOAD_SHUFFLE(m, k) \
m = LOAD_128((data + (k) * 16)); \
MY_rev32_for_LE(m); \
m = vreinterpretq_u32_u8( \
MY_rev32_for_LE( \
LOAD_128_8(data + (k) * 16))); \
// K array must be aligned for 16-bytes at least.
extern
MY_ALIGN(64)
const UInt32 SHA256_K_ARRAY[64];
#define K SHA256_K_ARRAY
#define SHA256_SU0(dest, src) dest = vsha256su0q_u32(dest, src);
#define SHA25G_SU1(dest, src2, src3) dest = vsha256su1q_u32(dest, src2, src3);
#define SHA256_SU1(dest, src2, src3) dest = vsha256su1q_u32(dest, src2, src3);
#define SM1(g0, g1, g2, g3) SHA256_SU0(g3, g0)
#define SM2(g0, g1, g2, g3) SHA25G_SU1(g2, g0, g1)
#define NNN(g0, g1, g2, g3)
#define SM1(m0, m1, m2, m3) SHA256_SU0(m3, m0)
#define SM2(m0, m1, m2, m3) SHA256_SU1(m2, m0, m1)
#define NNN(m0, m1, m2, m3)
#define R4(k, g0, g1, g2, g3, OP0, OP1) \
msg = vaddq_u32(g0, *(const v128 *) (const void *) &K[(k) * 4]); \
#define R4(k, m0, m1, m2, m3, OP0, OP1) \
msg = vaddq_u32(m0, *(const v128 *) (const void *) &K[(k) * 4]); \
tmp = state0; \
state0 = vsha256hq_u32( state0, state1, msg ); \
state1 = vsha256h2q_u32( state1, tmp, msg ); \
OP0(g0, g1, g2, g3); \
OP1(g0, g1, g2, g3); \
OP0(m0, m1, m2, m3); \
OP1(m0, m1, m2, m3); \
#define R16(k, OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7) \
@@ -379,8 +366,8 @@ void Z7_FASTCALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_
if (numBlocks == 0)
return;
state0 = LOAD_128(&state[0]);
state1 = LOAD_128(&state[4]);
state0 = LOAD_128_32(&state[0]);
state1 = LOAD_128_32(&state[4]);
do
{
@@ -408,8 +395,8 @@ void Z7_FASTCALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_
}
while (--numBlocks);
STORE_128(&state[0], state0);
STORE_128(&state[4], state1);
STORE_128_32(&state[0], state0);
STORE_128_32(&state[4], state1);
}
#endif // USE_HW_SHA
@@ -443,13 +430,10 @@ void Z7_FASTCALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_
#endif
#undef K
#undef RND2
#undef RND2_0
#undef RND2_1
#undef MY_rev32_for_LE
#undef NNN
#undef LOAD_128
#undef STORE_128
@@ -457,7 +441,7 @@ void Z7_FASTCALL Sha256_UpdateBlocks_HW(UInt32 state[8], const Byte *data, size_
#undef SM1
#undef SM2
#undef NNN
#undef R4
#undef R16
#undef PREPARE_STATE

View File

@@ -1,141 +1,268 @@
/* Sort.c -- Sort functions
2014-04-05 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
#include "Sort.h"
#include "CpuArch.h"
#define HeapSortDown(p, k, size, temp) \
{ for (;;) { \
size_t s = (k << 1); \
if (s > size) break; \
if (s < size && p[s + 1] > p[s]) s++; \
if (temp >= p[s]) break; \
p[k] = p[s]; k = s; \
} p[k] = temp; }
#if ( (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \
|| (defined(__clang__) && Z7_has_builtin(__builtin_prefetch)) \
)
// the code with prefetch is slow for small arrays on x86.
// So we disable prefetch for x86.
#ifndef MY_CPU_X86
// #pragma message("Z7_PREFETCH : __builtin_prefetch")
#define Z7_PREFETCH(a) __builtin_prefetch((a))
#endif
void HeapSort(UInt32 *p, size_t size)
{
if (size <= 1)
return;
p--;
{
size_t i = size / 2;
do
{
UInt32 temp = p[i];
size_t k = i;
HeapSortDown(p, k, size, temp)
}
while (--i != 0);
}
/*
do
{
size_t k = 1;
UInt32 temp = p[size];
p[size--] = p[1];
HeapSortDown(p, k, size, temp)
}
while (size > 1);
*/
while (size > 3)
{
UInt32 temp = p[size];
size_t k = (p[3] > p[2]) ? 3 : 2;
p[size--] = p[1];
p[1] = p[k];
HeapSortDown(p, k, size, temp)
}
{
UInt32 temp = p[size];
p[size] = p[1];
if (size > 2 && p[2] < temp)
{
p[1] = p[2];
p[2] = temp;
}
else
p[1] = temp;
}
}
#elif defined(_WIN32) // || defined(_MSC_VER) && (_MSC_VER >= 1200)
void HeapSort64(UInt64 *p, size_t size)
{
if (size <= 1)
return;
p--;
{
size_t i = size / 2;
do
{
UInt64 temp = p[i];
size_t k = i;
HeapSortDown(p, k, size, temp)
}
while (--i != 0);
}
/*
do
{
size_t k = 1;
UInt64 temp = p[size];
p[size--] = p[1];
HeapSortDown(p, k, size, temp)
}
while (size > 1);
*/
while (size > 3)
{
UInt64 temp = p[size];
size_t k = (p[3] > p[2]) ? 3 : 2;
p[size--] = p[1];
p[1] = p[k];
HeapSortDown(p, k, size, temp)
}
{
UInt64 temp = p[size];
p[size] = p[1];
if (size > 2 && p[2] < temp)
{
p[1] = p[2];
p[2] = temp;
}
else
p[1] = temp;
}
}
#include "7zWindows.h"
// NOTE: CLANG/GCC/MSVC can define different values for _MM_HINT_T0 / PF_TEMPORAL_LEVEL_1.
// For example, clang-cl can generate "prefetcht2" instruction for
// PreFetchCacheLine(PF_TEMPORAL_LEVEL_1) call.
// But we want to generate "prefetcht0" instruction.
// So for CLANG/GCC we must use __builtin_prefetch() in code branch above
// instead of PreFetchCacheLine() / _mm_prefetch().
// New msvc-x86 compiler generates "prefetcht0" instruction for PreFetchCacheLine() call.
// But old x86 cpus don't support "prefetcht0".
// So we will use PreFetchCacheLine(), only if we are sure that
// generated instruction is supported by all cpus of that isa.
#if defined(MY_CPU_AMD64) \
|| defined(MY_CPU_ARM64) \
|| defined(MY_CPU_IA64)
// we need to use additional braces for (a) in PreFetchCacheLine call, because
// PreFetchCacheLine macro doesn't use braces:
// #define PreFetchCacheLine(l, a) _mm_prefetch((CHAR CONST *) a, l)
// #pragma message("Z7_PREFETCH : PreFetchCacheLine")
#define Z7_PREFETCH(a) PreFetchCacheLine(PF_TEMPORAL_LEVEL_1, (a))
#endif
#endif // _WIN32
#define PREFETCH_NO(p,k,s,size)
#ifndef Z7_PREFETCH
#define SORT_PREFETCH(p,k,s,size)
#else
// #define PREFETCH_LEVEL 2 // use it if cache line is 32-bytes
#define PREFETCH_LEVEL 3 // it is fast for most cases (64-bytes cache line prefetch)
// #define PREFETCH_LEVEL 4 // it can be faster for big array (128-bytes prefetch)
#if PREFETCH_LEVEL == 0
#define SORT_PREFETCH(p,k,s,size)
#else // PREFETCH_LEVEL != 0
/*
#define HeapSortRefDown(p, vals, n, size, temp) \
{ size_t k = n; UInt32 val = vals[temp]; for (;;) { \
size_t s = (k << 1); \
if (s > size) break; \
if (s < size && vals[p[s + 1]] > vals[p[s]]) s++; \
if (val >= vals[p[s]]) break; \
p[k] = p[s]; k = s; \
} p[k] = temp; }
if defined(USE_PREFETCH_FOR_ALIGNED_ARRAY)
we prefetch one value per cache line.
Use it if array is aligned for cache line size (64 bytes)
or if array is small (less than L1 cache size).
void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size)
if !defined(USE_PREFETCH_FOR_ALIGNED_ARRAY)
we perfetch all cache lines that can be required.
it can be faster for big unaligned arrays.
*/
#define USE_PREFETCH_FOR_ALIGNED_ARRAY
// s == k * 2
#if 0 && PREFETCH_LEVEL <= 3 && defined(MY_CPU_X86_OR_AMD64)
// x86 supports (lea r1*8+offset)
#define PREFETCH_OFFSET(k,s) ((s) << PREFETCH_LEVEL)
#else
#define PREFETCH_OFFSET(k,s) ((k) << (PREFETCH_LEVEL + 1))
#endif
#if 1 && PREFETCH_LEVEL <= 3 && defined(USE_PREFETCH_FOR_ALIGNED_ARRAY)
#define PREFETCH_ADD_OFFSET 0
#else
// last offset that can be reqiured in PREFETCH_LEVEL step:
#define PREFETCH_RANGE ((2 << PREFETCH_LEVEL) - 1)
#define PREFETCH_ADD_OFFSET PREFETCH_RANGE / 2
#endif
#if PREFETCH_LEVEL <= 3
#ifdef USE_PREFETCH_FOR_ALIGNED_ARRAY
#define SORT_PREFETCH(p,k,s,size) \
{ const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_ADD_OFFSET; \
if (s2 <= size) { \
Z7_PREFETCH((p + s2)); \
}}
#else /* for unaligned array */
#define SORT_PREFETCH(p,k,s,size) \
{ const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE; \
if (s2 <= size) { \
Z7_PREFETCH((p + s2 - PREFETCH_RANGE)); \
Z7_PREFETCH((p + s2)); \
}}
#endif
#else // PREFETCH_LEVEL > 3
#ifdef USE_PREFETCH_FOR_ALIGNED_ARRAY
#define SORT_PREFETCH(p,k,s,size) \
{ const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE - 16 / 2; \
if (s2 <= size) { \
Z7_PREFETCH((p + s2 - 16)); \
Z7_PREFETCH((p + s2)); \
}}
#else /* for unaligned array */
#define SORT_PREFETCH(p,k,s,size) \
{ const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE; \
if (s2 <= size) { \
Z7_PREFETCH((p + s2 - PREFETCH_RANGE)); \
Z7_PREFETCH((p + s2 - PREFETCH_RANGE / 2)); \
Z7_PREFETCH((p + s2)); \
}}
#endif
#endif // PREFETCH_LEVEL > 3
#endif // PREFETCH_LEVEL != 0
#endif // Z7_PREFETCH
#if defined(MY_CPU_ARM64) \
/* || defined(MY_CPU_AMD64) */ \
/* || defined(MY_CPU_ARM) && !defined(_MSC_VER) */
// we want to use cmov, if cmov is very fast:
// - this cmov version is slower for clang-x64.
// - this cmov version is faster for gcc-arm64 for some fast arm64 cpus.
#define Z7_FAST_CMOV_SUPPORTED
#endif
#ifdef Z7_FAST_CMOV_SUPPORTED
// we want to use cmov here, if cmov is fast: new arm64 cpus.
// we want the compiler to use conditional move for this branch
#define GET_MAX_VAL(n0, n1, max_val_slow) if (n0 < n1) n0 = n1;
#else
// use this branch, if cpu doesn't support fast conditional move.
// it uses slow array access reading:
#define GET_MAX_VAL(n0, n1, max_val_slow) n0 = max_val_slow;
#endif
#define HeapSortDown(p, k, size, temp, macro_prefetch) \
{ \
for (;;) { \
UInt32 n0, n1; \
size_t s = k * 2; \
if (s >= size) { \
if (s == size) { \
n0 = p[s]; \
p[k] = n0; \
if (temp < n0) k = s; \
} \
break; \
} \
n0 = p[k * 2]; \
n1 = p[k * 2 + 1]; \
s += n0 < n1; \
GET_MAX_VAL(n0, n1, p[s]) \
if (temp >= n0) break; \
macro_prefetch(p, k, s, size) \
p[k] = n0; \
k = s; \
} \
p[k] = temp; \
}
/*
stage-1 : O(n) :
we generate intermediate partially sorted binary tree:
p[0] : it's additional item for better alignment of tree structure in memory.
p[1]
p[2] p[3]
p[4] p[5] p[6] p[7]
...
p[x] >= p[x * 2]
p[x] >= p[x * 2 + 1]
stage-2 : O(n)*log2(N):
we move largest item p[0] from head of tree to the end of array
and insert last item to sorted binary tree.
*/
// (p) must be aligned for cache line size (64-bytes) for best performance
void Z7_FASTCALL HeapSort(UInt32 *p, size_t size)
{
if (size <= 1)
if (size < 2)
return;
p--;
if (size == 2)
{
size_t i = size / 2;
const UInt32 a0 = p[0];
const UInt32 a1 = p[1];
const unsigned k = a1 < a0;
p[k] = a0;
p[k ^ 1] = a1;
return;
}
{
// stage-1 : O(n)
// we transform array to partially sorted binary tree.
size_t i = --size / 2;
// (size) now is the index of the last item in tree,
// if (i)
{
do
{
const UInt32 temp = p[i];
size_t k = i;
HeapSortDown(p, k, size, temp, PREFETCH_NO)
}
while (--i);
}
{
const UInt32 temp = p[0];
const UInt32 a1 = p[1];
if (temp < a1)
{
size_t k = 1;
p[0] = a1;
HeapSortDown(p, k, size, temp, PREFETCH_NO)
}
}
}
if (size < 3)
{
// size == 2
const UInt32 a0 = p[0];
p[0] = p[2];
p[2] = a0;
return;
}
if (size != 3)
{
// stage-2 : O(size) * log2(size):
// we move largest item p[0] from head to the end of array,
// and insert last item to sorted binary tree.
do
{
UInt32 temp = p[i];
HeapSortRefDown(p, vals, i, size, temp);
const UInt32 temp = p[size];
size_t k = p[2] < p[3] ? 3 : 2;
p[size--] = p[0];
p[0] = p[1];
p[1] = p[k];
HeapSortDown(p, k, size, temp, SORT_PREFETCH) // PREFETCH_NO
}
while (--i != 0);
while (size != 3);
}
do
{
UInt32 temp = p[size];
p[size--] = p[1];
HeapSortRefDown(p, vals, 1, size, temp);
const UInt32 a2 = p[2];
const UInt32 a3 = p[3];
const size_t k = a2 < a3;
p[2] = p[1];
p[3] = p[0];
p[k] = a3;
p[k ^ 1] = a2;
}
while (size > 1);
}
*/

View File

@@ -1,5 +1,5 @@
/* Threads.c -- multithreading library
2024-03-28 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -59,6 +59,100 @@ WRes Thread_Wait_Close(CThread *p)
return (res != 0 ? res : res2);
}
typedef struct MY_PROCESSOR_NUMBER {
WORD Group;
BYTE Number;
BYTE Reserved;
} MY_PROCESSOR_NUMBER, *MY_PPROCESSOR_NUMBER;
typedef struct MY_GROUP_AFFINITY {
#if defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION < 100000)
// KAFFINITY is not defined in old mingw
ULONG_PTR
#else
KAFFINITY
#endif
Mask;
WORD Group;
WORD Reserved[3];
} MY_GROUP_AFFINITY, *MY_PGROUP_AFFINITY;
typedef BOOL (WINAPI *Func_SetThreadGroupAffinity)(
HANDLE hThread,
CONST MY_GROUP_AFFINITY *GroupAffinity,
MY_PGROUP_AFFINITY PreviousGroupAffinity);
typedef BOOL (WINAPI *Func_GetThreadGroupAffinity)(
HANDLE hThread,
MY_PGROUP_AFFINITY GroupAffinity);
typedef BOOL (WINAPI *Func_GetProcessGroupAffinity)(
HANDLE hProcess,
PUSHORT GroupCount,
PUSHORT GroupArray);
Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
#if 0
#include <stdio.h>
#define PRF(x) x
/*
--
before call of SetThreadGroupAffinity()
GetProcessGroupAffinity return one group.
after call of SetThreadGroupAffinity():
GetProcessGroupAffinity return more than group,
if SetThreadGroupAffinity() was to another group.
--
GetProcessAffinityMask MS DOCs:
{
If the calling process contains threads in multiple groups,
the function returns zero for both affinity masks.
}
but tests in win10 with 2 groups (less than 64 cores total):
GetProcessAffinityMask() still returns non-zero affinity masks
even after SetThreadGroupAffinity() calls.
*/
static void PrintProcess_Info()
{
{
const
Func_GetProcessGroupAffinity fn_GetProcessGroupAffinity =
(Func_GetProcessGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
"GetProcessGroupAffinity");
if (fn_GetProcessGroupAffinity)
{
unsigned i;
USHORT GroupCounts[64];
USHORT GroupCount = Z7_ARRAY_SIZE(GroupCounts);
BOOL boolRes = fn_GetProcessGroupAffinity(GetCurrentProcess(),
&GroupCount, GroupCounts);
printf("\n====== GetProcessGroupAffinity : "
"boolRes=%u GroupCounts = %u :",
boolRes, (unsigned)GroupCount);
for (i = 0; i < GroupCount; i++)
printf(" %u", GroupCounts[i]);
printf("\n");
}
}
{
DWORD_PTR processAffinityMask, systemAffinityMask;
if (GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask))
{
PRF(printf("\n====== GetProcessAffinityMask : "
": processAffinityMask=%x, systemAffinityMask=%x\n",
(UInt32)processAffinityMask, (UInt32)systemAffinityMask);)
}
else
printf("\n==GetProcessAffinityMask FAIL");
}
}
#else
#ifndef USE_THREADS_CreateThread
// #define PRF(x)
#endif
#endif
WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
{
/* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
@@ -72,7 +166,43 @@ WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
unsigned threadId;
*p = (HANDLE)(_beginthreadex(NULL, 0, func, param, 0, &threadId));
#if 0 // 1 : for debug
{
DWORD_PTR prevMask;
DWORD_PTR affinity = 1 << 0;
prevMask = SetThreadAffinityMask(*p, (DWORD_PTR)affinity);
prevMask = prevMask;
}
#endif
#if 0 // 1 : for debug
{
/* win10: new thread will be created in same group that is assigned to parent thread
but affinity mask will contain all allowed threads of that group,
even if affinity mask of parent group is not full
win11: what group it will be created, if we have set
affinity of parent thread with ThreadGroupAffinity?
*/
const
Func_GetThreadGroupAffinity fn =
(Func_GetThreadGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
"GetThreadGroupAffinity");
if (fn)
{
// BOOL wres2;
MY_GROUP_AFFINITY groupAffinity;
memset(&groupAffinity, 0, sizeof(groupAffinity));
/* wres2 = */ fn(*p, &groupAffinity);
PRF(printf("\n==Thread_Create cur = %6u GetThreadGroupAffinity(): "
"wres2_BOOL = %u, group=%u mask=%x\n",
GetCurrentThreadId(),
wres2,
groupAffinity.Group,
(UInt32)groupAffinity.Mask);)
}
}
#endif
#endif
/* maybe we must use errno here, but probably GetLastError() is also OK. */
@@ -110,7 +240,84 @@ WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param
*/
}
{
DWORD prevSuspendCount = ResumeThread(h);
const DWORD prevSuspendCount = ResumeThread(h);
/* ResumeThread() returns:
0 : was_not_suspended
1 : was_resumed
-1 : error
*/
if (prevSuspendCount == (DWORD)-1)
wres = GetError();
}
}
/* maybe we must use errno here, but probably GetLastError() is also OK. */
return wres;
#endif
}
WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinityMask)
{
#ifdef USE_THREADS_CreateThread
UNUSED_VAR(group)
UNUSED_VAR(affinityMask)
return Thread_Create(p, func, param);
#else
/* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
HANDLE h;
WRes wres;
unsigned threadId;
h = (HANDLE)(_beginthreadex(NULL, 0, func, param, CREATE_SUSPENDED, &threadId));
*p = h;
wres = HandleToWRes(h);
if (h)
{
// PrintProcess_Info();
{
const
Func_SetThreadGroupAffinity fn =
(Func_SetThreadGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
"SetThreadGroupAffinity");
if (fn)
{
// WRes wres2;
MY_GROUP_AFFINITY groupAffinity, prev_groupAffinity;
memset(&groupAffinity, 0, sizeof(groupAffinity));
// groupAffinity.Mask must use only bits that supported by current group
// (groupAffinity.Mask = 0) means all allowed bits
groupAffinity.Mask = affinityMask;
groupAffinity.Group = (WORD)group;
// wres2 =
fn(h, &groupAffinity, &prev_groupAffinity);
/*
if (groupAffinity.Group == prev_groupAffinity.Group)
wres2 = wres2;
else
wres2 = wres2;
if (wres2 == 0)
{
wres2 = GetError();
PRF(printf("\n==SetThreadGroupAffinity error: %u\n", wres2);)
}
else
{
PRF(printf("\n==Thread_Create_With_Group::SetThreadGroupAffinity()"
" threadId = %6u"
" group=%u mask=%x\n",
threadId,
prev_groupAffinity.Group,
(UInt32)prev_groupAffinity.Mask);)
}
*/
}
}
{
const DWORD prevSuspendCount = ResumeThread(h);
/* ResumeThread() returns:
0 : was_not_suspended
1 : was_resumed
@@ -297,6 +504,13 @@ WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
return Thread_Create_With_CpuSet(p, func, param, NULL);
}
/*
WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinity)
{
UNUSED_VAR(group)
return Thread_Create_With_Affinity(p, func, param, affinity);
}
*/
WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, CAffinityMask affinity)
{
@@ -577,5 +791,22 @@ WRes AutoResetEvent_OptCreate_And_Reset(CAutoResetEvent *p)
return AutoResetEvent_CreateNotSignaled(p);
}
void ThreadNextGroup_Init(CThreadNextGroup *p, UInt32 numGroups, UInt32 startGroup)
{
// printf("\n====== ThreadNextGroup_Init numGroups = %x: startGroup=%x\n", numGroups, startGroup);
if (numGroups == 0)
numGroups = 1;
p->NumGroups = numGroups;
p->NextGroup = startGroup % numGroups;
}
UInt32 ThreadNextGroup_GetNext(CThreadNextGroup *p)
{
const UInt32 next = p->NextGroup;
p->NextGroup = (next + 1) % p->NumGroups;
return next;
}
#undef PRF
#undef Print

View File

@@ -1,5 +1,5 @@
/* XzCrc64Opt.c -- CRC64 calculation (optimized functions)
2023-12-08 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -235,7 +235,7 @@ CRC64_FUNC_PRE_BE(Z7_CRC64_NUM_TABLES_USE)
v = Q32BE(1, w1) ^ Q32BE(0, w0);
v ^= Q32BE(3, d1) ^ Q32BE(2, d0);
#endif
#elif
#else
#error Stop_Compiling_Bad_CRC64_NUM_TABLES
#endif
p += Z7_CRC64_NUM_TABLES_USE;

View File

@@ -1,5 +1,5 @@
/* XzDec.c -- Xz Decode
2024-03-01 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -59,7 +59,7 @@ unsigned Xz_ReadVarInt(const Byte *p, size_t maxSize, UInt64 *value)
for (i = 0; i < limit;)
{
Byte b = p[i];
const unsigned b = p[i];
*value |= (UInt64)(b & 0x7F) << (7 * i++);
if ((b & 0x80) == 0)
return (b == 0 && i != 1) ? 0 : i;
@@ -796,11 +796,10 @@ SRes Xz_ParseHeader(CXzStreamFlags *p, const Byte *buf)
static BoolInt Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *buf)
{
return indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2)
&& GetUi32(buf) == CrcCalc(buf + 4, 6)
&& flags == GetBe16(buf + 8)
&& buf[10] == XZ_FOOTER_SIG_0
&& buf[11] == XZ_FOOTER_SIG_1;
return indexSize == (((UInt64)GetUi32a(buf + 4) + 1) << 2)
&& GetUi32a(buf) == CrcCalc(buf + 4, 6)
&& flags == GetBe16a(buf + 8)
&& GetUi16a(buf + 10) == (XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8));
}
#define READ_VARINT_AND_CHECK(buf, pos, size, res) \
@@ -1166,7 +1165,7 @@ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen,
p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks);
p->indexPos = p->indexPreSize;
p->indexSize += p->indexPreSize;
Sha256_Final(&p->sha, p->shaDigest);
Sha256_Final(&p->sha, (Byte *)(void *)p->shaDigest32);
Sha256_Init(&p->sha);
p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize);
p->state = XZ_STATE_STREAM_INDEX;
@@ -1241,10 +1240,10 @@ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen,
break;
}
{
Byte digest[XZ_CHECK_SIZE_MAX];
UInt32 digest32[XZ_CHECK_SIZE_MAX / 4];
p->state = XZ_STATE_BLOCK_HEADER;
p->pos = 0;
if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0)
if (XzCheck_Final(&p->check, (void *)digest32) && memcmp(digest32, p->buf, checkSize) != 0)
return SZ_ERROR_CRC;
if (p->decodeOnlyOneBlock)
{
@@ -1289,12 +1288,12 @@ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen,
}
else
{
Byte digest[SHA256_DIGEST_SIZE];
UInt32 digest32[SHA256_DIGEST_SIZE / 4];
p->state = XZ_STATE_STREAM_INDEX_CRC;
p->indexSize += 4;
p->pos = 0;
Sha256_Final(&p->sha, digest);
if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0)
Sha256_Final(&p->sha, (void *)digest32);
if (memcmp(digest32, p->shaDigest32, SHA256_DIGEST_SIZE) != 0)
return SZ_ERROR_CRC;
}
}
@@ -1313,7 +1312,7 @@ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen,
const Byte *ptr = p->buf;
p->state = XZ_STATE_STREAM_FOOTER;
p->pos = 0;
if (CRC_GET_DIGEST(p->crc) != GetUi32(ptr))
if (CRC_GET_DIGEST(p->crc) != GetUi32a(ptr))
return SZ_ERROR_CRC;
}
break;
@@ -1343,7 +1342,7 @@ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen,
{
if (*src != 0)
{
if (((UInt32)p->padSize & 3) != 0)
if ((unsigned)p->padSize & 3)
return SZ_ERROR_NO_ARCHIVE;
p->pos = 0;
p->state = XZ_STATE_STREAM_HEADER;

View File

@@ -1,5 +1,5 @@
/* XzEnc.c -- Xz Encode
2024-03-01 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -411,6 +411,7 @@ static SRes SeqInFilter_Read(ISeqInStreamPtr pp, void *data, size_t *size)
}
}
Z7_FORCE_INLINE
static void SeqInFilter_Construct(CSeqInFilter *p)
{
p->buf = NULL;
@@ -418,6 +419,7 @@ static void SeqInFilter_Construct(CSeqInFilter *p)
p->vt.Read = SeqInFilter_Read;
}
Z7_FORCE_INLINE
static void SeqInFilter_Free(CSeqInFilter *p, ISzAllocPtr alloc)
{
if (p->StateCoder.p)
@@ -507,6 +509,7 @@ void XzFilterProps_Init(CXzFilterProps *p)
void XzProps_Init(CXzProps *p)
{
p->checkId = XZ_CHECK_CRC32;
p->numThreadGroups = 0;
p->blockSize = XZ_PROPS_BLOCK_SIZE_AUTO;
p->numBlockThreads_Reduced = -1;
p->numBlockThreads_Max = -1;
@@ -689,6 +692,7 @@ typedef struct
} CLzma2WithFilters;
Z7_FORCE_INLINE
static void Lzma2WithFilters_Construct(CLzma2WithFilters *p)
{
p->lzma2 = NULL;
@@ -712,6 +716,7 @@ static SRes Lzma2WithFilters_Create(CLzma2WithFilters *p, ISzAllocPtr alloc, ISz
}
Z7_FORCE_INLINE
static void Lzma2WithFilters_Free(CLzma2WithFilters *p, ISzAllocPtr alloc)
{
#ifdef USE_SUBBLOCK
@@ -1236,6 +1241,7 @@ SRes XzEnc_Encode(CXzEncHandle p, ISeqOutStreamPtr outStream, ISeqInStreamPtr in
}
p->mtCoder.numThreadsMax = (unsigned)props->numBlockThreads_Max;
p->mtCoder.numThreadGroups = props->numThreadGroups;
p->mtCoder.expectedDataSize = p->expectedDataSize;
RINOK(MtCoder_Code(&p->mtCoder))

View File

@@ -1,38 +1,39 @@
/* XzIn.c - Xz input
2023-09-07 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
#include <string.h>
#include "7zCrc.h"
#include "CpuArch.h"
#include "Xz.h"
#include "CpuArch.h"
/*
#define XZ_FOOTER_SIG_CHECK(p) (memcmp((p), XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0)
*/
#define XZ_FOOTER_SIG_CHECK(p) ((p)[0] == XZ_FOOTER_SIG_0 && (p)[1] == XZ_FOOTER_SIG_1)
#define XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(p) \
(GetUi16a((const Byte *)(const void *)(p) + 10) == \
(XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8)))
SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream)
{
Byte sig[XZ_STREAM_HEADER_SIZE];
UInt32 data32[XZ_STREAM_HEADER_SIZE / 4];
size_t processedSize = XZ_STREAM_HEADER_SIZE;
RINOK(SeqInStream_ReadMax(inStream, sig, &processedSize))
RINOK(SeqInStream_ReadMax(inStream, data32, &processedSize))
if (processedSize != XZ_STREAM_HEADER_SIZE
|| memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0)
|| memcmp(data32, XZ_SIG, XZ_SIG_SIZE) != 0)
return SZ_ERROR_NO_ARCHIVE;
return Xz_ParseHeader(p, sig);
return Xz_ParseHeader(p, (const Byte *)(const void *)data32);
}
#define READ_VARINT_AND_CHECK(buf, pos, size, res) \
{ const unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \
#define READ_VARINT_AND_CHECK(buf, size, res) \
{ const unsigned s = Xz_ReadVarInt(buf, size, res); \
if (s == 0) return SZ_ERROR_ARCHIVE; \
pos += s; }
size -= s; \
buf += s; \
}
SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes)
{
MY_ALIGN(4)
Byte header[XZ_BLOCK_HEADER_SIZE_MAX];
unsigned headerSize;
*headerSizeRes = 0;
@@ -57,8 +58,12 @@ SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex,
return XzBlock_Parse(p, header);
}
#define ADD_SIZE_CHECK(size, val) \
{ const UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; }
{ const UInt64 newSize = size + (val); \
if (newSize < size) return XZ_SIZE_OVERFLOW; \
size = newSize; \
}
UInt64 Xz_GetUnpackSize(const CXzStream *p)
{
@@ -82,76 +87,85 @@ UInt64 Xz_GetPackSize(const CXzStream *p)
return size;
}
/*
SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStreamPtr inStream)
{
return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f));
}
*/
static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc)
// input;
// CXzStream (p) is empty object.
// size != 0
// (size & 3) == 0
// (buf) is aligned for at least 4 bytes.
// output:
// p->numBlocks is number of allocated items in p->blocks
// p->blocks[*] values must be ignored, if function returns error.
static SRes Xz_ParseIndex(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc)
{
size_t numBlocks, pos = 1;
UInt32 crc;
size_t numBlocks;
if (size < 5 || buf[0] != 0)
return SZ_ERROR_ARCHIVE;
size -= 4;
crc = CrcCalc(buf, size);
if (crc != GetUi32(buf + size))
return SZ_ERROR_ARCHIVE;
{
const UInt32 crc = CrcCalc(buf, size);
if (crc != GetUi32a(buf + size))
return SZ_ERROR_ARCHIVE;
}
buf++;
size--;
{
UInt64 numBlocks64;
READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64)
READ_VARINT_AND_CHECK(buf, size, &numBlocks64)
// (numBlocks64) is 63-bit value, so we can calculate (numBlocks64 * 2):
if (numBlocks64 * 2 > size)
return SZ_ERROR_ARCHIVE;
if (numBlocks64 >= ((size_t)1 << (sizeof(size_t) * 8 - 1)) / sizeof(CXzBlockSizes))
return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE
numBlocks = (size_t)numBlocks64;
if (numBlocks != numBlocks64 || numBlocks * 2 > size)
return SZ_ERROR_ARCHIVE;
}
Xz_Free(p, alloc);
if (numBlocks != 0)
// Xz_Free(p, alloc); // it's optional, because (p) is empty already
if (numBlocks)
{
size_t i;
p->numBlocks = numBlocks;
p->blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks);
if (!p->blocks)
CXzBlockSizes *blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks);
if (!blocks)
return SZ_ERROR_MEM;
for (i = 0; i < numBlocks; i++)
p->blocks = blocks;
p->numBlocks = numBlocks;
// the caller will call Xz_Free() in case of error
do
{
CXzBlockSizes *block = &p->blocks[i];
READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize)
READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize)
if (block->totalSize == 0)
READ_VARINT_AND_CHECK(buf, size, &blocks->totalSize)
READ_VARINT_AND_CHECK(buf, size, &blocks->unpackSize)
if (blocks->totalSize == 0)
return SZ_ERROR_ARCHIVE;
blocks++;
}
while (--numBlocks);
}
while ((pos & 3) != 0)
if (buf[pos++] != 0)
if (size >= 4)
return SZ_ERROR_ARCHIVE;
while (size)
if (buf[--size])
return SZ_ERROR_ARCHIVE;
return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE;
return SZ_OK;
}
/*
static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc)
{
SRes res;
size_t size;
Byte *buf;
if (indexSize > ((UInt32)1 << 31))
return SZ_ERROR_UNSUPPORTED;
if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1)))
return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE
size = (size_t)indexSize;
if (size != indexSize)
return SZ_ERROR_UNSUPPORTED;
buf = (Byte *)ISzAlloc_Alloc(alloc, size);
if (!buf)
return SZ_ERROR_MEM;
res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED);
if (res == SZ_OK)
res = Xz_ReadIndex2(p, buf, size, alloc);
res = Xz_ParseIndex(p, buf, size, alloc);
ISzAlloc_Free(alloc, buf);
return res;
}
*/
static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size)
{
@@ -160,84 +174,102 @@ static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset,
/* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */
}
/*
in:
(*startOffset) is position in (stream) where xz_stream must be finished.
out:
if returns SZ_OK, then (*startOffset) is position in stream that shows start of xz_stream.
*/
static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc)
{
UInt64 indexSize;
Byte buf[XZ_STREAM_FOOTER_SIZE];
#define TEMP_BUF_SIZE (1 << 10)
UInt32 buf32[TEMP_BUF_SIZE / 4];
UInt64 pos = (UInt64)*startOffset;
if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE)
if ((pos & 3) || pos < XZ_STREAM_FOOTER_SIZE)
return SZ_ERROR_NO_ARCHIVE;
pos -= XZ_STREAM_FOOTER_SIZE;
RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE))
RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE))
if (!XZ_FOOTER_SIG_CHECK(buf + 10))
if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32))
{
UInt32 total = 0;
pos += XZ_STREAM_FOOTER_SIZE;
for (;;)
{
size_t i;
#define TEMP_BUF_SIZE (1 << 10)
Byte temp[TEMP_BUF_SIZE];
i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos;
// pos != 0
// (pos & 3) == 0
size_t i = pos >= TEMP_BUF_SIZE ? TEMP_BUF_SIZE : (size_t)pos;
pos -= i;
RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i))
total += (UInt32)i;
for (; i != 0; i--)
if (temp[i - 1] != 0)
RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, i))
i /= 4;
do
if (buf32[i - 1] != 0)
break;
if (i != 0)
{
if ((i & 3) != 0)
return SZ_ERROR_NO_ARCHIVE;
pos += i;
break;
}
if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16))
while (--i);
pos += i * 4;
#define XZ_STREAM_BACKWARD_READING_PAD_MAX (1 << 16)
// here we don't support rare case with big padding for xz stream.
// so we have padding limit for backward reading.
if ((UInt64)*startOffset - pos > XZ_STREAM_BACKWARD_READING_PAD_MAX)
return SZ_ERROR_NO_ARCHIVE;
if (i)
break;
}
// we try to open xz stream after skipping zero padding.
// ((UInt64)*startOffset == pos) is possible here!
if (pos < XZ_STREAM_FOOTER_SIZE)
return SZ_ERROR_NO_ARCHIVE;
pos -= XZ_STREAM_FOOTER_SIZE;
RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE))
if (!XZ_FOOTER_SIG_CHECK(buf + 10))
RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE))
if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32))
return SZ_ERROR_NO_ARCHIVE;
}
p->flags = (CXzStreamFlags)GetBe16(buf + 8);
p->flags = (CXzStreamFlags)GetBe16a(buf32 + 2);
if (!XzFlags_IsSupported(p->flags))
return SZ_ERROR_UNSUPPORTED;
{
/* to eliminate GCC 6.3 warning:
dereferencing type-punned pointer will break strict-aliasing rules */
const Byte *buf_ptr = buf;
if (GetUi32(buf_ptr) != CrcCalc(buf + 4, 6))
const UInt32 *buf_ptr = buf32;
if (GetUi32a(buf_ptr) != CrcCalc(buf32 + 1, 6))
return SZ_ERROR_ARCHIVE;
}
indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2;
if (pos < indexSize)
return SZ_ERROR_ARCHIVE;
pos -= indexSize;
RINOK(LookInStream_SeekTo(stream, pos))
RINOK(Xz_ReadIndex(p, stream, indexSize, alloc))
{
UInt64 totalSize = Xz_GetPackSize(p);
if (totalSize == XZ_SIZE_OVERFLOW
|| totalSize >= ((UInt64)1 << 63)
|| pos < totalSize + XZ_STREAM_HEADER_SIZE)
const UInt64 indexSize = ((UInt64)GetUi32a(buf32 + 1) + 1) << 2;
if (pos < indexSize)
return SZ_ERROR_ARCHIVE;
pos -= (totalSize + XZ_STREAM_HEADER_SIZE);
pos -= indexSize;
// v25.00: relaxed indexSize check. We allow big index table.
// if (indexSize > ((UInt32)1 << 31))
if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1)))
return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE
RINOK(LookInStream_SeekTo(stream, pos))
// RINOK(Xz_ReadIndex(p, stream, indexSize, alloc))
{
SRes res;
const size_t size = (size_t)indexSize;
// if (size != indexSize) return SZ_ERROR_UNSUPPORTED;
Byte *buf = (Byte *)ISzAlloc_Alloc(alloc, size);
if (!buf)
return SZ_ERROR_MEM;
res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED);
if (res == SZ_OK)
res = Xz_ParseIndex(p, buf, size, alloc);
ISzAlloc_Free(alloc, buf);
RINOK(res)
}
}
{
UInt64 total = Xz_GetPackSize(p);
if (total == XZ_SIZE_OVERFLOW || total >= ((UInt64)1 << 63))
return SZ_ERROR_ARCHIVE;
total += XZ_STREAM_HEADER_SIZE;
if (pos < total)
return SZ_ERROR_ARCHIVE;
pos -= total;
RINOK(LookInStream_SeekTo(stream, pos))
*startOffset = (Int64)pos;
}
@@ -246,7 +278,6 @@ static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startO
CSecToRead secToRead;
SecToRead_CreateVTable(&secToRead);
secToRead.realStream = stream;
RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt))
return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE;
}
@@ -257,8 +288,7 @@ static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startO
void Xzs_Construct(CXzs *p)
{
p->num = p->numAllocated = 0;
p->streams = 0;
Xzs_CONSTRUCT(p)
}
void Xzs_Free(CXzs *p, ISzAllocPtr alloc)
@@ -268,7 +298,7 @@ void Xzs_Free(CXzs *p, ISzAllocPtr alloc)
Xz_Free(&p->streams[i], alloc);
ISzAlloc_Free(alloc, p->streams);
p->num = p->numAllocated = 0;
p->streams = 0;
p->streams = NULL;
}
UInt64 Xzs_GetNumBlocks(const CXzs *p)
@@ -307,34 +337,49 @@ UInt64 Xzs_GetPackSize(const CXzs *p)
SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc)
{
Int64 endOffset = 0;
// it's supposed that CXzs object is empty here.
// if CXzs object is not empty, it will add new streams to that non-empty object.
// Xzs_Free(p, alloc); // it's optional call to empty CXzs object.
RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END))
*startOffset = endOffset;
for (;;)
{
CXzStream st;
SRes res;
Xz_Construct(&st);
Xz_CONSTRUCT(&st)
res = Xz_ReadBackward(&st, stream, startOffset, alloc);
// if (res == SZ_OK), then (*startOffset) is start offset of new stream if
// if (res != SZ_OK), then (*startOffset) is unchend or it's expected start offset of stream with error
st.startOffset = (UInt64)*startOffset;
RINOK(res)
// we must store (st) object to array, or we must free (st) local object.
if (res != SZ_OK)
{
Xz_Free(&st, alloc);
return res;
}
if (p->num == p->numAllocated)
{
const size_t newNum = p->num + p->num / 4 + 1;
void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream));
if (!data)
{
Xz_Free(&st, alloc);
return SZ_ERROR_MEM;
}
p->numAllocated = newNum;
if (p->num != 0)
memcpy(data, p->streams, p->num * sizeof(CXzStream));
ISzAlloc_Free(alloc, p->streams);
p->streams = (CXzStream *)data;
}
// we use direct copying of raw data from local variable (st) to object in array.
// so we don't need to call Xz_Free(&st, alloc) after copying and after p->num++
p->streams[p->num++] = st;
if (*startOffset == 0)
break;
RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset))
return SZ_OK;
// seek operation is optional:
// RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset))
if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK)
return SZ_ERROR_PROGRESS;
}
return SZ_OK;
}

View File

@@ -65,7 +65,7 @@
#define _wiltlg_STRINGIZE_imp(x) #x
#define _wiltlg_LSTRINGIZE(x) _wiltlg_LSTRINGIZE_imp1(x)
#define _wiltlg_LSTRINGIZE_imp1(x) _wiltlg_LSTRINGIZE_imp2(#x)
#define _wiltlg_LSTRINGIZE_imp2(s) L##s
#define _wiltlg_LSTRINGIZE_imp2(s) L"" #s
/*
Macro __TRACELOGGING_DEFINE_PROVIDER_STORAGE_LINK(name1, name2):
@@ -261,7 +261,8 @@ namespace details
public:
StoredCallContextInfo() WI_NOEXCEPT
{
::ZeroMemory(this, sizeof(*this));
// Suppress '-Wnontrivial-memcall' with 'static_cast'
::ZeroMemory(static_cast<void*>(this), sizeof(*this));
}
StoredCallContextInfo(StoredCallContextInfo&& other) WI_NOEXCEPT : StoredCallContextInfo()
@@ -1510,7 +1511,7 @@ public: \
#define __IMPLEMENT_CALLCONTEXT_CLASS(ActivityClassName) \
protected: \
ActivityClassName(_In_opt_ void**, PCSTR contextName, _In_opt_ _Printf_format_string_ PCSTR formatString, _In_opt_ va_list argList) : \
ActivityClassName(_In_opt_ void**, PCSTR contextName, _In_ _Printf_format_string_ PCSTR formatString, _In_opt_ va_list argList) : \
ActivityBase(contextName) \
{ \
GetCallContext()->SetMessage(formatString, argList); \
@@ -6444,7 +6445,13 @@ namespace details
if (*lastNamespaceNode)
{
root.swap((*lastNamespaceNode)->next);
// Delete everything from the current root to the lastNamespaceNode
// (inclusive), considering the possibility that they are the same. Continue
// processing from the node following lastNamespaceNode, if any. root will
// be made to point to that.
auto newRoot = wistd::move((*lastNamespaceNode)->next);
const auto toDelete = wistd::move(root);
root = wistd::move(newRoot);
}
else
{

View File

@@ -16,12 +16,13 @@
#include <WeakReference.h>
#include <combaseapi.h>
#include "result.h"
#include "win32_helpers.h"
#include "resource.h" // last to ensure _COMBASEAPI_H_ protected definitions are available
#if __has_include(<tuple>)
#if WIL_USE_STL && WI_HAS_INCLUDE(<tuple>, 1) // Tuple is C++11... assume available
#include <tuple>
#endif
#if __has_include(<type_traits>)
#if WIL_USE_STL && WI_HAS_INCLUDE(<type_traits>, 1) // Type traits is old... assume available
#include <type_traits>
#endif
@@ -2112,7 +2113,7 @@ wil::com_ptr_nothrow<Interface> CoGetClassObjectNoThrow(DWORD dwClsContext = CLS
return CoGetClassObjectNoThrow<Interface>(__uuidof(Class), dwClsContext);
}
#if __cpp_lib_apply && __has_include(<type_traits>)
#if __cpp_lib_apply && WIL_USE_STL && WI_HAS_INCLUDE(<type_traits>, 1)
/// @cond
namespace details
{
@@ -2246,7 +2247,7 @@ auto try_com_multi_query(IUnknown* obj)
}
#endif
#endif // __cpp_lib_apply && __has_include(<type_traits>)
#endif // __cpp_lib_apply && WI_HAS_INCLUDE(<type_traits>, 1)
#pragma endregion
@@ -3135,8 +3136,7 @@ void for_each_site(_In_opt_ IUnknown* siteInput, TLambda&& callback)
#endif // __IObjectWithSite_INTERFACE_DEFINED__
// if C++17 or greater
#if WIL_HAS_CXX_17
#if __cpp_deduction_guides >= 201703L
#ifdef WIL_ENABLE_EXCEPTIONS
/// @cond
namespace details
@@ -3303,14 +3303,14 @@ WI_NODISCARD auto make_range(IEnumXxx* enumPtr)
using enumerator_type = com_iterator<TActualStoredType, IEnumXxx>;
IEnumXxx* m_enumerator{};
wil::com_ptr<IEnumXxx> m_enumerator{};
iterator_range(IEnumXxx* enumPtr) : m_enumerator(enumPtr)
{
}
WI_NODISCARD auto begin()
{
return enumerator_type(m_enumerator);
return enumerator_type(m_enumerator.get());
}
WI_NODISCARD constexpr auto end() const noexcept
@@ -3322,9 +3322,131 @@ WI_NODISCARD auto make_range(IEnumXxx* enumPtr)
return iterator_range(enumPtr);
}
#endif // WIL_HAS_CXX_17
template <typename TEnum, typename = wistd::enable_if_t<wil::details::has_next_v<TEnum*>>>
auto make_range(const wil::com_ptr<TEnum>& e)
{
using Enumerated = typename wil::details::com_enumerator_traits<TEnum>::smart_result;
return wil::make_range<Enumerated>(e.get());
}
#ifdef __IShellItemArray_INTERFACE_DEFINED__
inline auto make_range(IShellItemArray* sia)
{
wil::com_ptr<IEnumShellItems> enumShellItems;
THROW_IF_FAILED(sia->EnumItems(&enumShellItems));
return make_range(enumShellItems);
}
inline auto make_range(const wil::com_ptr<IShellItemArray>& sia)
{
return make_range(sia.get());
}
#endif // __IShellItemArray_INTERFACE_DEFINED__
#endif // __cpp_deduction_guides >= 201703L
#endif // WIL_ENABLE_EXCEPTIONS
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
namespace details
{
inline void CoDisableCallCancellationNull()
{
(void)::CoDisableCallCancellation(nullptr);
}
} // namespace details
/** RAII support for making cross-apartment (or cross process) COM calls with a timeout applied to them.
* When this is active any timed out calls will fail with an RPC error code such as RPC_E_CALL_CANCELED.
* This is a shared timeout that applies to all calls made on the current thread for the lifetime of
* the wil::com_timeout object.
* A periodic timer is used to cancel calls that have been blocked too long. If multiple blocking calls
* are made, and multiple are timing out, then there may be a total delay of (timeoutInMilliseconds * N)
* where N is the number of calls.
~~~
{
auto timeout = wil::com_timeout(5000);
remote_object->BlockingCOMCall();
remote_object->AnotherBlockingCOMCall();
}
~~~
*/
template <typename err_policy>
class com_timeout_t
{
public:
com_timeout_t(DWORD timeoutInMilliseconds) : m_threadId(GetCurrentThreadId())
{
const HRESULT cancelEnablementResult = CoEnableCallCancellation(nullptr);
err_policy::HResult(cancelEnablementResult);
if (SUCCEEDED(cancelEnablementResult))
{
m_ensureDisable.activate();
m_timer.reset(CreateThreadpoolTimer(&com_timeout_t::timer_callback, this, nullptr));
err_policy::LastErrorIfFalse(static_cast<bool>(m_timer));
if (m_timer)
{
FILETIME ft = filetime::get_system_time();
ft = filetime::add(ft, filetime::convert_msec_to_100ns(timeoutInMilliseconds));
SetThreadpoolTimer(m_timer.get(), &ft, timeoutInMilliseconds, 0);
}
}
}
bool timed_out() const
{
return m_timedOut;
}
operator bool() const noexcept
{
// All construction calls must succeed to provide us with a non-null m_timer value.
return static_cast<bool>(m_timer);
}
private:
// Disable use of new as this class should only be declared on the stack, never the heap.
void* operator new(size_t) = delete;
void* operator new[](size_t) = delete;
// not copyable or movable because the timer_callback receives "this"
com_timeout_t(com_timeout_t const&) = delete;
void operator=(com_timeout_t const&) = delete;
static void __stdcall timer_callback(PTP_CALLBACK_INSTANCE /*instance*/, PVOID context, PTP_TIMER /*timer*/)
{
// The timer is waited upon during destruction so it is safe to rely on the this pointer in context.
com_timeout_t* self = static_cast<com_timeout_t*>(context);
if (SUCCEEDED(CoCancelCall(self->m_threadId, 0)))
{
self->m_timedOut = true;
}
}
wil::unique_call<decltype(&details::CoDisableCallCancellationNull), details::CoDisableCallCancellationNull, false> m_ensureDisable{};
DWORD m_threadId{};
bool m_timedOut{};
// The threadpool timer goes last so that it destructs first, waiting until the timer callback has completed.
wil::unique_threadpool_timer_nocancel m_timer;
};
// Error-policy driven forms of com_timeout
#ifdef WIL_ENABLE_EXCEPTIONS
//! COM timeout, errors throw exceptions (see @ref com_timeout_t for details)
using com_timeout = com_timeout_t<err_exception_policy>;
#endif
//! COM timeout, errors return error codes (see @ref com_timeout_t for details)
using com_timeout_nothrow = com_timeout_t<err_returncode_policy>;
//! COM timeout, errors fail-fast (see @ref com_timeout_t for details)
using com_timeout_failfast = com_timeout_t<err_failfast_policy>;
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
} // namespace wil
#endif

View File

@@ -69,15 +69,14 @@
/// @cond
#if defined(_MSVC_LANG)
#define __WI_SUPPRESS_4127_S \
__pragma(warning(push)) __pragma(warning(disable : 4127)) __pragma(warning(disable : 26498)) __pragma(warning(disable : 4245))
#define __WI_SUPPRESS_4127_E __pragma(warning(pop))
#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress : 28285)) __pragma(warning(suppress : 6504))
#define __WI_SUPPRESS_BREAKING_WARNINGS_S __pragma(warning(push)) __pragma(warning(disable : 4127 26498 4245 26814))
#define __WI_SUPPRESS_BREAKING_WARNINGS_E __pragma(warning(pop))
#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress : 28285 6504))
#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress : 26495))
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress : 26439))
#else
#define __WI_SUPPRESS_4127_S
#define __WI_SUPPRESS_4127_E
#define __WI_SUPPRESS_BREAKING_WARNINGS_S
#define __WI_SUPPRESS_BREAKING_WARNINGS_E
#define __WI_SUPPRESS_NULLPTR_ANALYSIS
#define __WI_SUPPRESS_NONINIT_ANALYSIS
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
@@ -306,6 +305,29 @@ static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode");
#error Must enable exceptions when WIL_EXCEPTION_MODE == 1
#endif
#ifdef WIL_DOXYGEN
/** This define is used to control whether or not WIL assumes safe access to the STL.
This define can be set manually (1 to enable, 0 to disable), otherwise heuristics will be applied in an attempt to
deduce whether or not the STL is available and can be safely used.
*/
#define WIL_USE_STL 1
#elif !defined(WIL_USE_STL)
#if !defined(WIL_ENABLE_EXCEPTIONS) || !defined(__has_include)
// Assume it's not safe to use the STL when:
// * Exceptions are not enabled, OR
// * We can't check for header presence
#define WIL_USE_STL 0
#else
// Check for several STL headers that have been around since the dawn of time
#if __has_include(<algorithm>) && __has_include(<exception>) && __has_include(<iterator>) && __has_include(<new>) && \
__has_include(<string>) && __has_include(<utility>) && __has_include(<vector>)
#define WIL_USE_STL 1
#else
#define WIL_USE_STL 0
#endif
#endif
#endif
/// @cond
#ifndef WIL_ITERATOR_DEBUG_LEVEL
// NOTE: See the definition of 'RESULT_DEBUG' for commentary on the use of 'WIL_KERNEL_MODE' below
@@ -373,19 +395,19 @@ check fails as opposed to the invalid parameter handler that the STL invokes. Th
#endif
/// @cond
#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703)
#define WIL_HAS_CXX_17 1
#else
#define WIL_HAS_CXX_17 0
#endif
// Until we'll have C++17 enabled in our code base, we're falling back to SAL
#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE
/// @endcond
/// @cond
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = nullptr
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = nullptr
// Uses the __has_include macro, if available. Otherwise uses a user-provided fallback. E.g. the fallback could always
// default to true or false, or it could do something like a C++ standard version check
#ifdef __has_include
#define WI_HAS_INCLUDE(header, fallback) __has_include(header)
#else
#define WI_HAS_INCLUDE(header, fallback) (fallback)
#endif
/// @endcond
//! @defgroup bitwise Bitwise Inspection and Manipulation
@@ -690,32 +712,32 @@ boolean, BOOLEAN, and classes with an explicit bool cast.
@param val The logical bool expression
@return A C++ bool representing the evaluation of `val`. */
template <typename T, __R_ENABLE_IF_IS_CLASS(T)>
_Post_satisfies_(return == static_cast<bool>(val)) __forceinline constexpr bool verify_bool(const T& val) WI_NOEXCEPT
_Post_satisfies_(return == static_cast<bool>(val)) inline constexpr bool verify_bool(const T& val) WI_NOEXCEPT
{
return static_cast<bool>(val);
}
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
__forceinline constexpr bool verify_bool(T /*val*/) WI_NOEXCEPT
inline constexpr bool verify_bool(T /*val*/) WI_NOEXCEPT
{
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
return false;
}
template <>
_Post_satisfies_(return == val) __forceinline constexpr bool verify_bool<bool>(bool val) WI_NOEXCEPT
_Post_satisfies_(return == val) inline constexpr bool verify_bool<bool>(bool val) WI_NOEXCEPT
{
return val;
}
template <>
_Post_satisfies_(return == (val != 0)) __forceinline constexpr bool verify_bool<int>(int val) WI_NOEXCEPT
_Post_satisfies_(return == (val != 0)) inline constexpr bool verify_bool<int>(int val) WI_NOEXCEPT
{
return (val != 0);
}
template <>
_Post_satisfies_(return == (val != 0)) __forceinline constexpr bool verify_bool<unsigned char>(unsigned char val) WI_NOEXCEPT
_Post_satisfies_(return == (val != 0)) inline constexpr bool verify_bool<unsigned char>(unsigned char val) WI_NOEXCEPT
{
return (val != 0);
}
@@ -726,7 +748,7 @@ accept any `int` value as long as that is the underlying typedef behind `BOOL`.
@param val The Win32 BOOL returning expression
@return A Win32 BOOL representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == val) __forceinline constexpr int verify_BOOL(T val) WI_NOEXCEPT
_Post_satisfies_(return == val) inline constexpr int verify_BOOL(T val) WI_NOEXCEPT
{
// Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL;
static_assert((wistd::is_same<T, int>::value), "Wrong Type: BOOL expected");
@@ -820,7 +842,7 @@ namespace details
: sizeof(val) == 2 ? static_cast<unsigned short>(val) \
: sizeof(val) == 4 ? static_cast<unsigned long>(val) \
: static_cast<unsigned long long>(val)) __pragma(warning(pop)))
#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val)-1)))
#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
#define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))
template <typename TVal, typename TFlags>

View File

@@ -26,6 +26,10 @@
#include "win32_helpers.h"
#include "resource.h"
#if WIL_USE_STL
#include <iterator>
#endif
namespace wil
{
//! Determines if a path is an extended length path that can be used to access paths longer than MAX_PATH.
@@ -403,7 +407,7 @@ struct next_entry_offset_iterator
using value_type = T;
using pointer = const T*;
using reference = const T&;
#ifdef _XUTILITY_
#if WIL_USE_STL
using iterator_category = ::std::forward_iterator_tag;
#endif

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