mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 22:29:43 +00:00
Compare commits
140 Commits
dev/lhecke
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21f91b1d7a | ||
|
|
990ed187d6 | ||
|
|
494bc5bd3f | ||
|
|
36f064cfc8 | ||
|
|
d0e94365d0 | ||
|
|
18d86bca09 | ||
|
|
0b4d3d5f89 | ||
|
|
4386bf07fd | ||
|
|
aa256ad5c9 | ||
|
|
b715008de3 | ||
|
|
9278873d76 | ||
|
|
59dc5eff42 | ||
|
|
bcac9993cb | ||
|
|
a8e0b9ccf6 | ||
|
|
37aba3157c | ||
|
|
0bd19e9cfc | ||
|
|
fc606d2bae | ||
|
|
4259ce535f | ||
|
|
d9131c6889 | ||
|
|
0ce654eaf6 | ||
|
|
fc586e2662 | ||
|
|
b520da26d4 | ||
|
|
a7e47b711a | ||
|
|
2c97c0555d | ||
|
|
5e8e10fdc0 | ||
|
|
bc6f3e2275 | ||
|
|
6196a3d0f7 | ||
|
|
4aa1624cd2 | ||
|
|
c699a468c9 | ||
|
|
0576e5bc1e | ||
|
|
544452dad4 | ||
|
|
00f46e400a | ||
|
|
4eb06fee07 | ||
|
|
d2c3cfd164 | ||
|
|
5fdfd51209 | ||
|
|
7b50f12a78 | ||
|
|
6e5827add5 | ||
|
|
17a55da0f9 | ||
|
|
0cb3426281 | ||
|
|
1482fd4ecd | ||
|
|
837215b206 | ||
|
|
93d592bb41 | ||
|
|
760daa642e | ||
|
|
1f71568c2a | ||
|
|
f93347ed4b | ||
|
|
040f26175f | ||
|
|
0a91023df8 | ||
|
|
0a9cbd09d8 | ||
|
|
cd8c12586b | ||
|
|
ef960558b3 | ||
|
|
47d9a87a23 | ||
|
|
b07589e7a8 | ||
|
|
7d790c7c61 | ||
|
|
9ec8584f86 | ||
|
|
dbbc581154 | ||
|
|
1de142b4b1 | ||
|
|
d1a1f9836e | ||
|
|
e006f75f6c | ||
|
|
6dd9c468eb | ||
|
|
cbb4a0a01c | ||
|
|
eabebc4cb2 | ||
|
|
b3f41626b4 | ||
|
|
56cfb77c6d | ||
|
|
628e99f5d2 | ||
|
|
3b4ee83ed1 | ||
|
|
ce92b18507 | ||
|
|
fd1b1c35b4 | ||
|
|
5ff8b80358 | ||
|
|
0a7c2585a2 | ||
|
|
a40a4ea094 | ||
|
|
1cb3445834 | ||
|
|
516ade54cb | ||
|
|
056af83994 | ||
|
|
7b39d24913 | ||
|
|
249fe2aca1 | ||
|
|
408f3e2bfd | ||
|
|
e0dae59f38 | ||
|
|
60ac45c239 | ||
|
|
b439925acc | ||
|
|
131728b17d | ||
|
|
37e2bc0caa | ||
|
|
735ef2823e | ||
|
|
faf21acbc7 | ||
|
|
9b21b78fee | ||
|
|
4c018efd64 | ||
|
|
1ef497970f | ||
|
|
65219d40ce | ||
|
|
bf44b6c360 | ||
|
|
1511d2c2ad | ||
|
|
7fd9c5c789 | ||
|
|
9c1436775e | ||
|
|
4a40c4329a | ||
|
|
06c07ab50d | ||
|
|
0fd8dc575f | ||
|
|
9074e9d6a8 | ||
|
|
2478c643f4 | ||
|
|
0199ca33dd | ||
|
|
7c0d6d95db | ||
|
|
edfa3ea0f0 | ||
|
|
9ab2870bc3 | ||
|
|
ac865e6666 | ||
|
|
746cf1f148 | ||
|
|
2c452e0fd6 | ||
|
|
d4c1dad0fe | ||
|
|
9d0180a554 | ||
|
|
2fab9866b2 | ||
|
|
0bafab9a0f | ||
|
|
f6a415511a | ||
|
|
7d8455d4eb | ||
|
|
dd5f2ad755 | ||
|
|
9a0d784500 | ||
|
|
24450a3dd7 | ||
|
|
9007fc2894 | ||
|
|
07c7167535 | ||
|
|
dfb52331f8 | ||
|
|
5174c96d6d | ||
|
|
8149bd0dd0 | ||
|
|
8207f26bcc | ||
|
|
c0774dcda8 | ||
|
|
114c2b44d1 | ||
|
|
c7be9a2dbf | ||
|
|
450eec48de | ||
|
|
a8582978af | ||
|
|
50fe0f82ce | ||
|
|
9bafa52c65 | ||
|
|
39108a7a1b | ||
|
|
d730cfda9f | ||
|
|
2f43886ab5 | ||
|
|
295cd17b02 | ||
|
|
54ef019a46 | ||
|
|
21fa303a3d | ||
|
|
7851c96812 | ||
|
|
e02d46bdd2 | ||
|
|
75f7ae4bec | ||
|
|
6372baa0d3 | ||
|
|
cbc8eed476 | ||
|
|
d74440f0f1 | ||
|
|
65accfd5c3 | ||
|
|
6095d3c0cc | ||
|
|
0a2b660e64 |
2
.github/actions/spelling/allow/allow.txt
vendored
2
.github/actions/spelling/allow/allow.txt
vendored
@@ -27,6 +27,7 @@ gje
|
||||
godbolt
|
||||
hyperlinking
|
||||
hyperlinks
|
||||
Kbds
|
||||
kje
|
||||
libfuzzer
|
||||
liga
|
||||
@@ -43,6 +44,7 @@ mkmk
|
||||
mnt
|
||||
mru
|
||||
nje
|
||||
NTMTo
|
||||
notwrapped
|
||||
ogonek
|
||||
overlined
|
||||
|
||||
2
.github/actions/spelling/allow/apis.txt
vendored
2
.github/actions/spelling/allow/apis.txt
vendored
@@ -96,6 +96,7 @@ IGraphics
|
||||
IImage
|
||||
IInheritable
|
||||
IMap
|
||||
imm
|
||||
IMonarch
|
||||
IObject
|
||||
iosfwd
|
||||
@@ -174,6 +175,7 @@ PALLOC
|
||||
PATINVERT
|
||||
PEXPLICIT
|
||||
PICKFOLDERS
|
||||
PINPUT
|
||||
pmr
|
||||
ptstr
|
||||
QUERYENDSESSION
|
||||
|
||||
1
.github/actions/spelling/excludes.txt
vendored
1
.github/actions/spelling/excludes.txt
vendored
@@ -128,3 +128,4 @@
|
||||
^XamlStyler\.json$
|
||||
ignore$
|
||||
Resources/(?!en)
|
||||
^\.vsconfig$
|
||||
|
||||
37
.github/actions/spelling/expect/expect.txt
vendored
37
.github/actions/spelling/expect/expect.txt
vendored
@@ -9,7 +9,6 @@ ABORTIFHUNG
|
||||
ACCESSTOKEN
|
||||
acidev
|
||||
ACIOSS
|
||||
ACover
|
||||
acp
|
||||
actctx
|
||||
ACTCTXW
|
||||
@@ -38,6 +37,7 @@ ANSISYS
|
||||
ANSISYSRC
|
||||
ANSISYSSC
|
||||
answerback
|
||||
ANSWERBACKMESSAGE
|
||||
antialiasing
|
||||
ANull
|
||||
anycpu
|
||||
@@ -86,6 +86,7 @@ Autowrap
|
||||
AVerify
|
||||
awch
|
||||
azurecr
|
||||
AZZ
|
||||
backgrounded
|
||||
Backgrounder
|
||||
backgrounding
|
||||
@@ -142,8 +143,8 @@ BTNFACE
|
||||
bufferout
|
||||
buffersize
|
||||
buflen
|
||||
buildtransitive
|
||||
buildsystems
|
||||
buildtransitive
|
||||
BValue
|
||||
bytebuffer
|
||||
cac
|
||||
@@ -179,7 +180,6 @@ CFuzz
|
||||
cgscrn
|
||||
chafa
|
||||
changelists
|
||||
charinfo
|
||||
CHARSETINFO
|
||||
chh
|
||||
chshdng
|
||||
@@ -263,7 +263,6 @@ consolegit
|
||||
consolehost
|
||||
CONSOLEIME
|
||||
consoleinternal
|
||||
Consoleroot
|
||||
CONSOLESETFOREGROUND
|
||||
consoletaeftemplates
|
||||
consoleuwp
|
||||
@@ -385,7 +384,7 @@ DECCIR
|
||||
DECCKM
|
||||
DECCKSR
|
||||
DECCOLM
|
||||
DECCRA
|
||||
deccra
|
||||
DECCTR
|
||||
DECDC
|
||||
DECDHL
|
||||
@@ -397,7 +396,7 @@ DECEKBD
|
||||
DECERA
|
||||
DECFI
|
||||
DECFNK
|
||||
DECFRA
|
||||
decfra
|
||||
DECGCI
|
||||
DECGCR
|
||||
DECGNL
|
||||
@@ -726,7 +725,6 @@ GHIJKL
|
||||
gitcheckin
|
||||
gitfilters
|
||||
gitlab
|
||||
gitmodules
|
||||
gle
|
||||
GLOBALFOCUS
|
||||
GLYPHENTRY
|
||||
@@ -866,6 +864,7 @@ INLINEPREFIX
|
||||
inproc
|
||||
Inputkeyinfo
|
||||
Inputreadhandledata
|
||||
INPUTSCOPE
|
||||
INSERTMODE
|
||||
INTERACTIVITYBASE
|
||||
INTERCEPTCOPYPASTE
|
||||
@@ -915,6 +914,7 @@ Keymapping
|
||||
keyscan
|
||||
keystate
|
||||
keyups
|
||||
Kickstart
|
||||
KILLACTIVE
|
||||
KILLFOCUS
|
||||
kinda
|
||||
@@ -1020,7 +1020,6 @@ lstatus
|
||||
lstrcmp
|
||||
lstrcmpi
|
||||
LTEXT
|
||||
LTLTLTLTL
|
||||
ltsc
|
||||
LUID
|
||||
luma
|
||||
@@ -1115,7 +1114,6 @@ msrc
|
||||
MSVCRTD
|
||||
MTSM
|
||||
Munged
|
||||
munges
|
||||
murmurhash
|
||||
muxes
|
||||
myapplet
|
||||
@@ -1217,7 +1215,6 @@ ntlpcapi
|
||||
ntm
|
||||
ntrtl
|
||||
ntstatus
|
||||
NTSYSCALLAPI
|
||||
nttree
|
||||
nturtl
|
||||
ntuser
|
||||
@@ -1233,6 +1230,7 @@ NUMSCROLL
|
||||
NUnit
|
||||
nupkg
|
||||
NVIDIA
|
||||
NVT
|
||||
OACR
|
||||
objbase
|
||||
ocolor
|
||||
@@ -1293,6 +1291,7 @@ parms
|
||||
PATCOPY
|
||||
pathcch
|
||||
PATTERNID
|
||||
pbstr
|
||||
pcb
|
||||
pcch
|
||||
PCCHAR
|
||||
@@ -1378,9 +1377,11 @@ POSTCHARBREAKS
|
||||
POSX
|
||||
POSXSCROLL
|
||||
POSYSCROLL
|
||||
ppbstr
|
||||
PPEB
|
||||
ppf
|
||||
ppidl
|
||||
pprg
|
||||
PPROC
|
||||
ppropvar
|
||||
ppsi
|
||||
@@ -1407,6 +1408,7 @@ processenv
|
||||
processhost
|
||||
PROCESSINFOCLASS
|
||||
PRODEXT
|
||||
Productize
|
||||
PROPERTYID
|
||||
PROPERTYKEY
|
||||
propertyval
|
||||
@@ -1523,7 +1525,6 @@ rftp
|
||||
rgbi
|
||||
RGBQUAD
|
||||
rgbs
|
||||
rgci
|
||||
rgfae
|
||||
rgfte
|
||||
rgn
|
||||
@@ -1601,6 +1602,7 @@ SELECTALL
|
||||
SELECTEDFONT
|
||||
SELECTSTRING
|
||||
Selfhosters
|
||||
Serbo
|
||||
SERVERDLL
|
||||
SETACTIVE
|
||||
SETBUDDYINT
|
||||
@@ -1695,6 +1697,7 @@ srcsrv
|
||||
SRCSRVTRG
|
||||
srctool
|
||||
srect
|
||||
SRGS
|
||||
srvinit
|
||||
srvpipe
|
||||
ssa
|
||||
@@ -1813,6 +1816,7 @@ TITLEISLINKNAME
|
||||
TJson
|
||||
TLambda
|
||||
TLDP
|
||||
tldr
|
||||
TLEN
|
||||
TMAE
|
||||
TMPF
|
||||
@@ -1828,8 +1832,6 @@ TOPDOWNDIB
|
||||
TOpt
|
||||
tosign
|
||||
touchpad
|
||||
Tpp
|
||||
Tpqrst
|
||||
tracelogging
|
||||
traceviewpp
|
||||
trackbar
|
||||
@@ -1842,7 +1844,6 @@ triaging
|
||||
TRIMZEROHEADINGS
|
||||
trx
|
||||
tsa
|
||||
tsattrs
|
||||
tsgr
|
||||
tsm
|
||||
TStr
|
||||
@@ -1954,7 +1955,6 @@ VPACKMANIFESTDIRECTORY
|
||||
VPR
|
||||
VREDRAW
|
||||
vsc
|
||||
vsconfig
|
||||
vscprintf
|
||||
VSCROLL
|
||||
vsdevshell
|
||||
@@ -1971,10 +1971,8 @@ vswhere
|
||||
vtapp
|
||||
VTE
|
||||
VTID
|
||||
vtio
|
||||
vtmode
|
||||
vtpipeterm
|
||||
vtpt
|
||||
VTRGB
|
||||
VTRGBTo
|
||||
vtseq
|
||||
@@ -1996,7 +1994,6 @@ wcswidth
|
||||
wddm
|
||||
wddmcon
|
||||
WDDMCONSOLECONTEXT
|
||||
WDK
|
||||
wdm
|
||||
webpage
|
||||
websites
|
||||
@@ -2070,7 +2067,6 @@ winuserp
|
||||
WINVER
|
||||
wistd
|
||||
wmain
|
||||
wmemory
|
||||
WMSZ
|
||||
wnd
|
||||
WNDALLOC
|
||||
@@ -2137,6 +2133,7 @@ XBUTTONDOWN
|
||||
XBUTTONUP
|
||||
XCast
|
||||
XCENTER
|
||||
xchar
|
||||
xcopy
|
||||
XCount
|
||||
xdy
|
||||
@@ -2168,6 +2165,7 @@ yact
|
||||
YCast
|
||||
YCENTER
|
||||
YCount
|
||||
yizz
|
||||
YLimit
|
||||
YPan
|
||||
YSubstantial
|
||||
@@ -2181,3 +2179,4 @@ ZCtrl
|
||||
ZWJs
|
||||
ZYXWVU
|
||||
ZYXWVUTd
|
||||
zzf
|
||||
|
||||
@@ -22,6 +22,7 @@ vcvars\w*
|
||||
ROY\sG\.\sBIV
|
||||
!(?:(?i)ESC)!\[
|
||||
!(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
|
||||
(?i)rgb:[a-z0-9]{2,4}/[a-z0-9]{2,4}/[a-z0-9]{2,4}
|
||||
|
||||
# SSE intrinsics like "_mm_subs_epu16"
|
||||
\b_mm(?:|256|512)_\w+\b
|
||||
|
||||
33
.github/workflows/similarIssues.yml
vendored
33
.github/workflows/similarIssues.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: GitGudSimilarIssues comments
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
getSimilarIssues:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
message: ${{ steps.getBody.outputs.message }}
|
||||
steps:
|
||||
- id: getBody
|
||||
uses: craigloewen-msft/GitGudSimilarIssues@main
|
||||
with:
|
||||
issueTitle: ${{ github.event.issue.title }}
|
||||
issueBody: ${{ github.event.issue.body }}
|
||||
repo: ${{ github.repository }}
|
||||
similaritytolerance: "0.8"
|
||||
add-comment:
|
||||
needs: getSimilarIssues
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: needs.getSimilarIssues.outputs.message != ''
|
||||
steps:
|
||||
- name: Add comment
|
||||
run: gh issue comment "$NUMBER" --repo "$REPO" --body "$BODY"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
BODY: ${{ needs.getSimilarIssues.outputs.message }}
|
||||
62
.vsconfig
62
.vsconfig
@@ -1,35 +1,51 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"components": [
|
||||
"Microsoft.VisualStudio.Component.Roslyn.Compiler",
|
||||
"Microsoft.Component.MSBuild",
|
||||
"Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
|
||||
"Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime",
|
||||
"Microsoft.VisualStudio.Component.SQL.CLR",
|
||||
"Microsoft.VisualStudio.Component.CoreEditor",
|
||||
"Microsoft.VisualStudio.Workload.CoreEditor",
|
||||
"Microsoft.VisualStudio.Workload.Universal",
|
||||
"Microsoft.VisualStudio.Workload.NativeDesktop",
|
||||
"Microsoft.VisualStudio.Workload.ManagedDesktop",
|
||||
"Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
|
||||
"Microsoft.Net.Component.4.8.SDK",
|
||||
"Microsoft.Net.Component.4.7.2.TargetingPack",
|
||||
"Microsoft.VisualStudio.Component.TextTemplating",
|
||||
"Microsoft.VisualStudio.Component.NuGet",
|
||||
"Microsoft.VisualStudio.Component.Roslyn.Compiler",
|
||||
"Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
|
||||
"Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
|
||||
"Microsoft.Component.MSBuild",
|
||||
"Microsoft.VisualStudio.Component.ManagedDesktop.Core",
|
||||
"Microsoft.Net.Component.4.TargetingPack",
|
||||
"Microsoft.Net.Component.4.5.TargetingPack",
|
||||
"Microsoft.NetCore.Component.Runtime.8.0",
|
||||
"Microsoft.NetCore.Component.SDK",
|
||||
"Microsoft.VisualStudio.Component.AppInsights.Tools",
|
||||
"Microsoft.Net.Component.4.8.TargetingPack",
|
||||
"Microsoft.VisualStudio.Component.DiagnosticTools",
|
||||
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
|
||||
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
|
||||
"Microsoft.NetCore.Component.Runtime.6.0",
|
||||
"Microsoft.VisualStudio.Component.ClassDesigner",
|
||||
"Microsoft.VisualStudio.Component.GraphDocument",
|
||||
"Microsoft.VisualStudio.Component.CodeMap",
|
||||
"Microsoft.VisualStudio.Component.VC.CoreIde",
|
||||
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
"Microsoft.VisualStudio.Component.Graphics",
|
||||
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
|
||||
"Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging",
|
||||
"Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs",
|
||||
"Microsoft.ComponentGroup.Blend",
|
||||
"Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native",
|
||||
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
|
||||
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
"Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake",
|
||||
"Microsoft.VisualStudio.Component.Vcpkg",
|
||||
"Microsoft.Component.NetFX.Native",
|
||||
"Microsoft.VisualStudio.Component.Graphics",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.Xamarin",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.ARM64EC",
|
||||
"Microsoft.VisualStudio.Component.UWP.VC.ARM64EC",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
|
||||
"Microsoft.VisualStudio.Component.VC.ASAN",
|
||||
"Microsoft.VisualStudio.Component.VC.v143.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.v143.ARM64",
|
||||
"Microsoft.VisualStudio.Component.UWP.VC.ARM64",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.ARM",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v143",
|
||||
"Microsoft.VisualStudio.Component.UWP.VC.ARM64"
|
||||
]
|
||||
"Microsoft.VisualStudio.Workload.NativeDesktop",
|
||||
"Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard",
|
||||
"Microsoft.VisualStudio.Workload.Universal"
|
||||
],
|
||||
"extensions": []
|
||||
}
|
||||
|
||||
34
.wt.json
Normal file
34
.wt.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"$version": "1.0.0",
|
||||
"snippets":
|
||||
[
|
||||
{
|
||||
"input": "bx\r",
|
||||
"name": "Build project",
|
||||
"description": "Build the project in the CWD"
|
||||
},
|
||||
{
|
||||
"input": "bz\r",
|
||||
"name": "Build solution, incremental",
|
||||
"description": "Just build changes to the solution"
|
||||
},
|
||||
{
|
||||
"input": "bcz\r",
|
||||
"name": "Clean & build solution",
|
||||
"icon": "\uE8e6",
|
||||
"description": "Start over. Go get your coffee. "
|
||||
},
|
||||
{
|
||||
"input": "nuget push -ApiKey az -source TerminalDependencies %userprofile%\\Downloads",
|
||||
"name": "Upload package to nuget feed",
|
||||
"icon": "\uE898",
|
||||
"description": "Go download a .nupkg, put it in ~/Downloads, and use this to push to our private feed."
|
||||
},
|
||||
{
|
||||
"input": "runut /name:**\u001b[D",
|
||||
"name": "Run a test",
|
||||
"icon": "",
|
||||
"description": "Enter the name of a test to run"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -41,6 +41,11 @@
|
||||
-->
|
||||
<MSBuildCacheIdenticalDuplicateOutputPatterns>$(MSBuildCacheIdenticalDuplicateOutputPatterns);bin\**</MSBuildCacheIdenticalDuplicateOutputPatterns>
|
||||
|
||||
<!--
|
||||
vcpkg is invoked for each and every vcxproj and each invocation uses the same base dir for logging, so allow these duplicate "outputs".
|
||||
-->
|
||||
<MSBuildCacheIdenticalDuplicateOutputPatterns>$(MSBuildCacheIdenticalDuplicateOutputPatterns);obj\*\vcpkg\**</MSBuildCacheIdenticalDuplicateOutputPatterns>
|
||||
|
||||
<!-- version of MSBuildCache is not part of the cache key -->
|
||||
<PackagesConfigFile>$(MSBuildThisFileDirectory)\dep\nuget\packages.config</PackagesConfigFile>
|
||||
<MSBuildCacheIgnoredInputPatterns>$(MSBuildCacheIgnoredInputPatterns);$(PackagesConfigFile)</MSBuildCacheIgnoredInputPatterns>
|
||||
|
||||
36
NOTICE.md
36
NOTICE.md
@@ -48,6 +48,42 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
## chromium/base/numerics
|
||||
|
||||
**Source**: [https://github.com/chromium/chromium/tree/master/base/numerics](https://github.com/chromium/chromium/tree/master/base/numerics)
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"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 COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS 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.
|
||||
```
|
||||
|
||||
## \{fmt\}
|
||||
|
||||
**Source**: [https://github.com/fmtlib/fmt](https://github.com/fmtlib/fmt)
|
||||
|
||||
11
README.md
11
README.md
@@ -1,5 +1,7 @@
|
||||

|
||||
|
||||
[](https://dev.azure.com/shine-oss/terminal/_build/latest?definitionId=1&branchName=main)
|
||||
|
||||
# Welcome to the Windows Terminal, Console and Command-Line repo
|
||||
|
||||
This repository contains the source code for:
|
||||
@@ -145,15 +147,6 @@ _Learn more about the [types of Windows Terminal distributions](https://learn.mi
|
||||
The plan for the Windows Terminal [is described here](/doc/roadmap-2023.md) and
|
||||
will be updated as the project proceeds.
|
||||
|
||||
## Project Build Status
|
||||
|
||||
Project|Build Status
|
||||
---|---
|
||||
Terminal|[](https://dev.azure.com/ms/terminal/_build?definitionId=136)
|
||||
ColorTool|
|
||||
|
||||
---
|
||||
|
||||
## Terminal & Console Overview
|
||||
|
||||
Please take a few minutes to review the overview below before diving into the
|
||||
|
||||
@@ -56,7 +56,15 @@ Dies ist ein Open Source-Projekt, und wir freuen uns über die Teilnahme der Com
|
||||
<ReleaseNotes>
|
||||
Version __VERSION_NUMBER__
|
||||
|
||||
Weitere Einzelheiten finden Sie auf der Seite der GitHub-Veröffentlichungen.
|
||||
– Wir haben umgeschrieben, wie Konsolenanwendungen im Terminal gehostet werden! Melden Sie alle auftretenden Fehler.
|
||||
- Terminal unterstützt jetzt Sixels!
|
||||
- Sie können jetzt ein angedocktes Fenster öffnen, das Ausschnitte von Befehlen enthält, die Sie gespeichert haben, um sie später zu verwenden.
|
||||
- Für Benutzer der Eingabeaufforderung der neuesten Version von Windows 11 wird möglicherweise ein „Kurzer Tipp“-Symbol angezeigt, das installierbare Software von WinGet
|
||||
vorschlägt
|
||||
- Ausgewählter Text wird jetzt viel sichtbarer (und anpassbarer!)
|
||||
- Eine Reihe von Zuverlässigkeitsfehlern, Komfortproblemen und Ärgernissen wurden behoben.
|
||||
|
||||
Weitere Informationen finden Sie auf unserer GitHub-Releaseseite.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,6 +56,13 @@ This is an open source project and we welcome community participation. To partic
|
||||
<ReleaseNotes _locID="App_ReleaseNotes">
|
||||
<!-- _locComment_text="{MaxLength=1500} {Locked=__VERSION_NUMBER__} App Release Note" -->Version __VERSION_NUMBER__
|
||||
|
||||
- We've rewritten how console applications are hosted inside Terminal! Please report any bugs you encounter.
|
||||
- Terminal now supports Sixels!
|
||||
- You can now open a docked panel containing snippets of commands you have saved to use later
|
||||
- Command Prompt users on the latest Windows 11 release may see a "quick tip" icon that suggests installable software from WinGet
|
||||
- Selected text will now be much more visible (and customizable!)
|
||||
- A number of reliabilty bugs, convenience issues and annoyances have been fixed.
|
||||
|
||||
Please see our GitHub releases page for additional details.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
|
||||
@@ -56,7 +56,14 @@ Este es un proyecto de fuente abierta y animamos a la comunidad a participar. Pa
|
||||
<ReleaseNotes>
|
||||
Versión __VERSION_NUMBER__
|
||||
|
||||
Para más información, consulte nuestra página de versiones de GitHub.
|
||||
- Hemos reescrito cómo se hospedan las aplicaciones de consola en Terminal. Informe de los errores que encuentre.
|
||||
- Terminal ahora admite sixeles.
|
||||
- Ahora puede abrir un panel acoplado que contenga fragmentos de comandos que haya guardado para usarlos más adelante
|
||||
- Los usuarios del símbolo del sistema de la versión más reciente de Windows 11 pueden ver un icono de "sugerencia rápida" que sugiere software instalable de WinGet
|
||||
- El texto seleccionado ahora será mucho más visible (y personalizable)
|
||||
- Se han corregido varios errores de fiabilidad, problemas de comodidad y molestias.
|
||||
|
||||
Consulte la página de versiones de GitHub para más información.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@ Il s’agit d’un projet open source et nous vous invitons à participer dans l
|
||||
<ReleaseNotes>
|
||||
Version __VERSION_NUMBER__
|
||||
|
||||
Consultez la page des versions de GitHub pour plus d’informations.
|
||||
- Nous avons réécrit la manière dont les applications de console sont hébergées dans Terminal ! Veuillez signaler tout bug que vous rencontrez.
|
||||
- Le terminal prend désormais en charge Sixels !
|
||||
- Vous pouvez désormais ouvrir un panneau ancré contenant des extraits de commandes que vous avez enregistrées pour les utiliser ultérieurement
|
||||
- Les utilisateurs de l'invite de commande sur la dernière version de Windows 11 peuvent voir une icône « astuce rapide » qui suggère un logiciel installable à partir de WinGet
|
||||
- Le texte sélectionné sera désormais beaucoup plus visible (et personnalisable !)
|
||||
- Un certain nombre de bugs de fiabilité, de problèmes de commodité et de désagréments ont été corrigés.
|
||||
|
||||
Veuillez consulter notre page de versions GitHub pour plus de détails.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -54,9 +54,16 @@ Si tratta di un progetto open source e la partecipazione della community è molt
|
||||
|
||||
</DevStudio>
|
||||
<ReleaseNotes>
|
||||
Versione __VERSION_NUMBER__
|
||||
Versione __VERSION_NUMBER__
|
||||
|
||||
Per ulteriori dettagli, consulta la nostra pagina delle versioni di GitHub.
|
||||
- È stato riscritto il modo in cui le applicazioni della console vengono ospitate all'interno di Terminale. Segnala eventuali bug riscontrati.
|
||||
- Terminal supporta ora Sixel.
|
||||
- È ora possibile aprire un pannello ancorato contenente frammenti di comandi salvati per usarli in seguito
|
||||
- Gli utenti del prompt dei comandi nella versione più recente di Windows 11 potrebbero visualizzare un'icona di "suggerimento rapido" che consiglia il software installabile da WinGet
|
||||
- Il testo selezionato sarà ora molto più visibile, oltre che personalizzabile.
|
||||
- Sono stati risolti diversi bug di affidabilità, problemi di praticità e fastidi.
|
||||
|
||||
Per altri dettagli, vedi la pagina delle versioni di GitHub.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
バージョン __VERSION_NUMBER__
|
||||
|
||||
詳細については、GitHub リリースのページをご覧ください。
|
||||
- ターミナル内でのコンソール アプリケーションのホスト方法を書き換えました。発生したバグを報告してください。
|
||||
- ターミナルで Sixels がサポートされるようになりました。
|
||||
- 後で使用するために保存したコマンドのスニペットを含むドッキング パネルを開けるようになりました
|
||||
- 最新の Windows 11 リリースのコマンド プロンプト ユーザーには、WinGet からインストール可能なソフトウェアを提案する "クイック ヒント" アイコンが表示される場合があります
|
||||
- 選択したテキストが大幅に見やすくなりました (カスタマイズも可能です)
|
||||
- 信頼性に関するバグ、利便性の問題、不快な問題の多くが修正されました。
|
||||
|
||||
詳細については、GitHub リリース ページをご覧ください。
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,6 +56,13 @@
|
||||
<ReleaseNotes>
|
||||
버전 __VERSION_NUMBER__
|
||||
|
||||
- 콘솔 애플리케이션이 터미널 내에서 호스팅되는 방법을 다시 작성했습니다! 발생한 버그를 보고하세요.
|
||||
- 터미널에서 이제 Sixels를 지원합니다!
|
||||
- 이제 나중에 사용하기 위해 저장한 명령 조각이 포함된 도킹된 패널을 열 수 있습니다.
|
||||
- 최신 Windows 11 릴리스의 명령 프롬프트 사용자는 WinGet에서 설치 가능한 소프트웨어를 제안하는 "빠른 팁" 아이콘을 볼 수 있습니다.
|
||||
- 이제 선택한 텍스트가 훨씬 더 잘 표시됩니다(사용자 지정도 가능!).
|
||||
- 여러 신뢰성 버그, 편의 문제 및 성가신 사항이 수정되었습니다.
|
||||
|
||||
자세한 내용은 GitHub 릴리스 페이지를 참조하세요.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
|
||||
@@ -56,7 +56,14 @@ Este é um projeto de código aberto e a participação da comunidade é bem-vin
|
||||
<ReleaseNotes>
|
||||
Versão __VERSION_NUMBER__
|
||||
|
||||
Consulte nossa página de lançamentos do GitHub para obter detalhes adicionais.
|
||||
- Reescrevemos a forma como os aplicativos de console são hospedados no Terminal! Certifique-se de reportar os bugs que você encontrar.
|
||||
- O terminal agora é compatível com o Sixels!
|
||||
- Agora você pode abrir um painel acoplado contendo snippets de comandos que você salvou para usar mais tarde
|
||||
- Os usuários do Prompt de Comando na versão mais recente do Windows 11 podem ver um ícone de "dica rápida", que sugere softwares instaláveis a partir do WinGet
|
||||
- O texto selecionado agora ficará muito mais visível (e personalizável!)
|
||||
- Vários bugs de confiabilidade, problemas de conveniência e incômodos foram resolvidos.
|
||||
|
||||
Confira nossa página de lançamentos no GitHub para obter mais detalhes.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
|
||||
|
||||
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
|
||||
- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!
|
||||
- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!!
|
||||
- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! !
|
||||
- Ä ņϋmъ℮ŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
|
||||
Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!!
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
|
||||
|
||||
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
|
||||
- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!
|
||||
- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!!
|
||||
- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! !
|
||||
- Ä ņϋmъ℮ŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
|
||||
Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!!
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
|
||||
|
||||
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
|
||||
- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!
|
||||
- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!!
|
||||
- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! !
|
||||
- Ä ņϋmъ℮ŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
|
||||
Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!!
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Версия __VERSION_NUMBER__
|
||||
|
||||
Дополнительные сведения см. на странице «Выпуски GitHub».
|
||||
– Мы переписали, как консольные приложения размещаются внутри Терминала! Сообщайте о любых ошибках, с которыми вы столкнулись.
|
||||
– Терминал теперь поддерживает форматы Sixel!
|
||||
– Теперь вы можете открыть закрепленную панель, содержащую фрагменты команд, которые вы сохранили для использования в дальнейшем
|
||||
– Пользователи командной строки в новейшем выпуске Windows 11 могут увидеть значок "краткой подсказки", который предлагает устанавливаемые программы из WinGet
|
||||
– Выделенный текст теперь станет более видимым (и настраиваемым!)
|
||||
– Исправлено несколько ошибок надежности, проблем с удобством, а также устранены раздражающие моменты.
|
||||
|
||||
Дополнительные сведения см. на странице выпусков GitHub.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -54,7 +54,14 @@
|
||||
|
||||
</DevStudio>
|
||||
<ReleaseNotes>
|
||||
版本 __VERSION_NUMBER__
|
||||
Version __VERSION_NUMBER__
|
||||
|
||||
- 我们已改变主机应用程序在终端内的托管方式!请报告遇到的任何 bug。
|
||||
- 终端现在支持 Sixels!
|
||||
- 现在可以打开一个停靠面板,其中包含已保存供以后使用的命令片段
|
||||
- 最新 Windows 11 版本上的命令提示用户可能会看到“快速提示”图标,该图标建议从 WinGet 安装软件
|
||||
- 所选文本现在将具有更高的可见性(和可自定义性!)
|
||||
- 修复了许多可靠性 bug、便利性问题和令人烦恼的问题。
|
||||
|
||||
有关其他详细信息,请参阅我们的 GitHub 发布页面。
|
||||
</ReleaseNotes>
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
版本 __VERSION_NUMBER__
|
||||
|
||||
如需詳細資訊,請參閱我們的 GitHub 版本頁面。
|
||||
- 我們已重寫主機應用程式在終端機內託管的方式!請報告您遇到的錯誤。
|
||||
- 終端機現在支援 Sixels!
|
||||
- 現在,您可以開啟包含已儲存命令程式碼片段的固定面板,以供稍後使用
|
||||
- 最新 Windows 11 版本中的 [命令提示] 使用者可能會看到「快速提示」圖示,建議可自 WinGet 安裝的軟體
|
||||
- 選取的文字現在會更明顯 (且可自訂!)
|
||||
- 已修正一些可靠性錯誤、便利性問題和令人困擾的問題。
|
||||
|
||||
如需更多詳細資訊,請參閱我們的 GitHub 發行頁面。
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@ Dies ist ein Open Source-Projekt, und wir freuen uns über die Teilnahme an der
|
||||
<ReleaseNotes>
|
||||
Version __VERSION_NUMBER__
|
||||
|
||||
Weitere Einzelheiten finden Sie auf der Seite der GitHub-Veröffentlichungen.
|
||||
– Terminal speichert jetzt den Inhalt des Fensters, wenn Sie die Sitzungswiederherstellung verwenden.
|
||||
– Sie können jetzt mehrere Schriftarten gleichzeitig verwenden.
|
||||
– Kästchenzeichnende Zeichen werden jetzt pixelgenau gerendert.
|
||||
– Die Verwendung eines IME innerhalb des Terminals wurde erheblich verbessert.
|
||||
– Die Farbschemas in Ihrer JSON-Datei sind jetzt viel einfacher.
|
||||
– Eine Reihe von Fehlern im Zusammenhang mit der URL-Verarbeitung, Zeilen mit doppelter Breite, Zeilenumbruch und mehr wurden behoben.
|
||||
|
||||
Weitere Informationen finden Sie auf unserer GitHub-Releaseseite.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,6 +56,13 @@ This is an open source project and we welcome community participation. To partic
|
||||
<ReleaseNotes _locID="App_ReleaseNotes">
|
||||
<!-- _locComment_text="{MaxLength=1500} {Locked=__VERSION_NUMBER__} App Release Note" -->Version __VERSION_NUMBER__
|
||||
|
||||
- Terminal will now remember the contents of the window when you use session restoration.
|
||||
- You can now use multiple fonts at the same time.
|
||||
- Box-drawing characters are now rendered with pixel perfection.
|
||||
- The experience of using an IME inside Terminal has been significantly improved.
|
||||
- The color schemes inside your JSON file will now be much simpler.
|
||||
- A number of bugs around URL handling, double-width rows, line wrapping, and more have been fixed.
|
||||
|
||||
Please see our GitHub releases page for additional details.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
|
||||
@@ -56,7 +56,14 @@ Este es un proyecto de fuente abierta y animamos a la comunidad a participar. Pa
|
||||
<ReleaseNotes>
|
||||
Versión __VERSION_NUMBER__
|
||||
|
||||
Para más información, consulte nuestra página de versiones de GitHub.
|
||||
- Terminal recordará ahora el contenido de la ventana cuando use la restauración de la sesión.
|
||||
- Ahora puede usar varias fuentes al mismo tiempo.
|
||||
- Los caracteres que dibujan recuadros ahora se representan con precisión de píxel.
|
||||
- Se ha mejorado significativamente la experiencia de utilizar un IME dentro de Terminal.
|
||||
- Las combinaciones de colores dentro del archivo JSON ahora serán mucho más sencillas.
|
||||
- Se han corregido varios errores relacionados con el control de direcciones URL, las filas de ancho doble, el ajuste de líneas y mucho más.
|
||||
|
||||
Consulte la página de versiones de GitHub para más información.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@ Il s’agit d’un projet open source et nous encourageons la participation à l
|
||||
<ReleaseNotes>
|
||||
Version __VERSION_NUMBER__
|
||||
|
||||
Consultez la page des versions de GitHub pour plus d’informations.
|
||||
- Le terminal mémorisera désormais le contenu de la fenêtre lorsque vous utiliserez la restauration de session.
|
||||
- Vous pouvez désormais utiliser plusieurs polices en même temps.
|
||||
- Les personnages dessinés en boîte sont désormais rendus avec une perfection de pixel.
|
||||
- L'expérience d'utilisation d'un IME dans le Terminal a été considérablement améliorée.
|
||||
- Les schémas de couleurs à l'intérieur de votre fichier JSON seront désormais beaucoup plus simples.
|
||||
- Un certain nombre de bugs concernant la gestion des URL, les lignes à double largeur, le retour à la ligne, etc. ont été corrigés.
|
||||
|
||||
Veuillez consulter notre page de versions GitHub pour plus de détails.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -54,9 +54,16 @@ Si tratta di un progetto open source e la partecipazione della community è molt
|
||||
|
||||
</DevStudio>
|
||||
<ReleaseNotes>
|
||||
Versione __VERSION_NUMBER__
|
||||
Versione __VERSION_NUMBER__
|
||||
|
||||
Per ulteriori dettagli, consulta la nostra pagina delle versioni di GitHub.
|
||||
- Il terminale ricorda ora il contenuto della finestra quando si usa il ripristino della sessione.
|
||||
- È ora possibile usare più tipi di carattere contemporaneamente.
|
||||
- I caratteri tracciati vengono ora sottoposti a rendering con pixel di perfezionamento.
|
||||
- L'esperienza di utilizzo di un IME all'interno di Terminale è stata notevolmente migliorata.
|
||||
- Le combinazioni di colori all'interno del file JSON saranno ora molto più semplici.
|
||||
- Sono stati corretti alcuni bug relativi alla gestione degli URL, alle righe a doppia larghezza, al ritorno a capo delle righe e altro ancora.
|
||||
|
||||
Per altri dettagli, vedi la pagina delle versioni di GitHub.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
バージョン __VERSION_NUMBER__
|
||||
|
||||
詳細については、GitHub リリースのページをご覧ください。
|
||||
- セッションの復元を使用すると、ターミナルがウィンドウの内容を記憶するようになりました。
|
||||
- 複数のフォントを同時に使用できるようになりました。
|
||||
- ボックス描画文字がピクセル単位の精度でレンダリングされるようになりました。
|
||||
- ターミナル内での IME の使用エクスペリエンスが大幅に改善されました。
|
||||
- JSON ファイル内の配色がはるかにシンプルになりました。
|
||||
- URL 処理、二重幅の行、行の折り返しなどに関するいくつかのバグが修正されました。
|
||||
|
||||
詳細については、GitHub リリース ページをご覧ください。
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,6 +56,13 @@
|
||||
<ReleaseNotes>
|
||||
버전 __VERSION_NUMBER__
|
||||
|
||||
- 터미널은 이제 세션 복원을 사용할 때 창의 내용을 기억합니다.
|
||||
- 이제 여러 글꼴을 동시에 사용할 수 있습니다.
|
||||
- 상자 그리기 캐릭터가 이제 픽셀 완성도로 렌더링됩니다.
|
||||
- 터미널 내에서 IME를 사용하는 환경이 크게 개선되었습니다.
|
||||
- 이제 JSON 파일 내의 색 구성표가 훨씬 더 간단해집니다.
|
||||
- URL 처리, 이중 너비 행, 줄 바꿈 등과 관련된 여러 버그가 수정되었습니다.
|
||||
|
||||
자세한 내용은 GitHub 릴리스 페이지를 참조하세요.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
|
||||
@@ -56,7 +56,14 @@ Este é um projeto de código aberto e a participação da comunidade é bem-vin
|
||||
<ReleaseNotes>
|
||||
Versão __VERSION_NUMBER__
|
||||
|
||||
Consulte nossa página de lançamentos do GitHub para obter detalhes adicionais.
|
||||
- O terminal agora se lembra do conteúdo da janela quando você usa a restauração de sessão.
|
||||
- Agora você pode usar várias fontes ao mesmo tempo.
|
||||
- Os caracteres da caixa de desenho agora são renderizados com a perfeição de pixels.
|
||||
- A experiência de usar uma IME dentro do Terminal foi significativamente aprimorada.
|
||||
- Os esquemas de cores dentro do seu arquivo JSON agora estão muito mais simples.
|
||||
- Foram corrigidos vários bugs envolvendo o tratamento de URLs, linhas de largura dupla, quebra de linha automática e muito mais.
|
||||
|
||||
Confira nossa página de lançamentos no GitHub para obter mais detalhes.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
|
||||
|
||||
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
|
||||
- Ŧēгмíйǻŀ шιļł ñσщ řėmėmвзґ τђз ςоńţëηťŝ σƒ ŧћé ẅιⁿδőщ ẅђеή ýóύ ŭš℮ şεššîóŋ řėşτŏѓдτіόŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ύоџ ςàⁿ ŋóώ ũşэ múľŧìφľё ƒоʼnťş àт ťħе ѕâmз тìме. !!! !!! !!! !!! !!!
|
||||
- Вό×-ðгăшĭиġ ¢ĥаяäςтеřѕ äřę ηоẁ ѓëńđêяεď ẁϊτђ φïжêĺ φėŗƒēςŧΐøй. !!! !!! !!! !!! !!! !!! !
|
||||
- Ťħέ ĕхφêŕï℮ηĉε ŏƒ ύѕïйġ ǻʼn ÎМË îńšïďê Τєřmíлäļ нαŝ ьēέň ѕιĝήîƒіčäπţŀý ĩмφґθνзđ. !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ťĥę čöℓοг şçђėmęš ιʼnśΐδê убџѓ ĴŠОИ ƒϊŀε ωĭŀł ʼnθω вз мúçĥ ѕїмρℓёґ. !!! !!! !!! !!! !!! !!! !!
|
||||
- Á ήũmьéŕ òƒ вµġŝ άřòūñδ ÛҐĿ ħàŋδľįйģ, ðőџъŀε-ŵĭďτђ ŗōẁš, ŀϊπė ẃяąрρΐηğ, âⁿđ мŏř℮ ĥāνě везŋ ƒï×έð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
|
||||
Ρļēªšê ŝέė őůг ĜīтĤųъ яëŀεäśēś рдġэ ƒõя ãδðìτϊöňãł δèτâĩĺѕ. !!! !!! !!! !!! !!! !!!
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
|
||||
|
||||
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
|
||||
- Ŧēгмíйǻŀ шιļł ñσщ řėmėmвзґ τђз ςоńţëηťŝ σƒ ŧћé ẅιⁿδőщ ẅђеή ýóύ ŭš℮ şεššîóŋ řėşτŏѓдτіόŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ύоџ ςàⁿ ŋóώ ũşэ múľŧìφľё ƒоʼnťş àт ťħе ѕâmз тìме. !!! !!! !!! !!! !!!
|
||||
- Вό×-ðгăшĭиġ ¢ĥаяäςтеřѕ äřę ηоẁ ѓëńđêяεď ẁϊτђ φïжêĺ φėŗƒēςŧΐøй. !!! !!! !!! !!! !!! !!! !
|
||||
- Ťħέ ĕхφêŕï℮ηĉε ŏƒ ύѕïйġ ǻʼn ÎМË îńšïďê Τєřmíлäļ нαŝ ьēέň ѕιĝήîƒіčäπţŀý ĩмφґθνзđ. !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ťĥę čöℓοг şçђėmęš ιʼnśΐδê убџѓ ĴŠОИ ƒϊŀε ωĭŀł ʼnθω вз мúçĥ ѕїмρℓёґ. !!! !!! !!! !!! !!! !!! !!
|
||||
- Á ήũmьéŕ òƒ вµġŝ άřòūñδ ÛҐĿ ħàŋδľįйģ, ðőџъŀε-ŵĭďτђ ŗōẁš, ŀϊπė ẃяąрρΐηğ, âⁿđ мŏř℮ ĥāνě везŋ ƒï×έð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
|
||||
Ρļēªšê ŝέė őůг ĜīтĤųъ яëŀεäśēś рдġэ ƒõя ãδðìτϊöňãł δèτâĩĺѕ. !!! !!! !!! !!! !!! !!!
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
|
||||
|
||||
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
|
||||
- Ŧēгмíйǻŀ шιļł ñσщ řėmėmвзґ τђз ςоńţëηťŝ σƒ ŧћé ẅιⁿδőщ ẅђеή ýóύ ŭš℮ şεššîóŋ řėşτŏѓдτіόŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ύоџ ςàⁿ ŋóώ ũşэ múľŧìφľё ƒоʼnťş àт ťħе ѕâmз тìме. !!! !!! !!! !!! !!!
|
||||
- Вό×-ðгăшĭиġ ¢ĥаяäςтеřѕ äřę ηоẁ ѓëńđêяεď ẁϊτђ φïжêĺ φėŗƒēςŧΐøй. !!! !!! !!! !!! !!! !!! !
|
||||
- Ťħέ ĕхφêŕï℮ηĉε ŏƒ ύѕïйġ ǻʼn ÎМË îńšïďê Τєřmíлäļ нαŝ ьēέň ѕιĝήîƒіčäπţŀý ĩмφґθνзđ. !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
- Ťĥę čöℓοг şçђėmęš ιʼnśΐδê убџѓ ĴŠОИ ƒϊŀε ωĭŀł ʼnθω вз мúçĥ ѕїмρℓёґ. !!! !!! !!! !!! !!! !!! !!
|
||||
- Á ήũmьéŕ òƒ вµġŝ άřòūñδ ÛҐĿ ħàŋδľįйģ, ðőџъŀε-ŵĭďτђ ŗōẁš, ŀϊπė ẃяąрρΐηğ, âⁿđ мŏř℮ ĥāνě везŋ ƒï×έð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
|
||||
|
||||
Ρļēªšê ŝέė őůг ĜīтĤųъ яëŀεäśēś рдġэ ƒõя ãδðìτϊöňãł δèτâĩĺѕ. !!! !!! !!! !!! !!! !!!
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
Версия __VERSION_NUMBER__
|
||||
|
||||
Дополнительные сведения см. на странице «Выпуски GitHub».
|
||||
– Терминал теперь будет запоминать содержимое окна при восстановлении сеанса.
|
||||
– Теперь вы можете использовать несколько шрифтов одновременно.
|
||||
– Символы псевдографики теперь отрисовываются с пиксельной точностью.
|
||||
– Значительно улучшена возможность использования IME внутри Терминала.
|
||||
– Цветовые схемы в JSON-файле теперь будут намного проще.
|
||||
– Исправлено несколько ошибок в обработке URL-адресов, строках двойной ширины, переносе строк и т. д.
|
||||
|
||||
Дополнительные сведения см. на странице выпусков GitHub.
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -54,7 +54,14 @@
|
||||
|
||||
</DevStudio>
|
||||
<ReleaseNotes>
|
||||
版本 __VERSION_NUMBER__
|
||||
Version __VERSION_NUMBER__
|
||||
|
||||
- 现在,使用会话还原时,终端将记住窗口的内容。
|
||||
- 现在可以同时使用多种字体。
|
||||
- 现在以像素为单位呈现框绘图字符。
|
||||
- 在终端内使用输入法的体验已得到显著提升。
|
||||
- JSON 文件中的配色方案现在要简单得多。
|
||||
- 已修复有关 URL 处理、双倍行宽、换行等大量 bug。
|
||||
|
||||
有关其他详细信息,请参阅我们的 GitHub 发布页面。
|
||||
</ReleaseNotes>
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
<ReleaseNotes>
|
||||
版本 __VERSION_NUMBER__
|
||||
|
||||
如需詳細資訊,請參閱我們的 GitHub 版本頁面。
|
||||
- 當您使用工作階段還原時,終端機現在會記住視窗的內容。
|
||||
- 現在您可以同時使用多個字型。
|
||||
- 製表格圖字元現在會以完美像素模式呈現。
|
||||
- 在終端機內使用 IME 的體驗已大幅改善。
|
||||
- JSON 檔案內的色彩配置現在將變得更簡單了。
|
||||
- 已修正一些 URL 處理、雙寬度列、換行等相關錯誤。
|
||||
|
||||
如需更多詳細資訊,請參閱我們的 GitHub 發行頁面。
|
||||
</ReleaseNotes>
|
||||
<ScreenshotCaptions>
|
||||
<!-- Valid length: 200 character limit, up to 9 elements per platform -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"instanceUrl": "https://microsoft.visualstudio.com",
|
||||
"projectName": "OS",
|
||||
"areaPath": "OS\\Windows Client and Services\\ADEPT\\E4D-Engineered for Developers\\SHINE\\Terminal",
|
||||
"areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\Terminal",
|
||||
"notificationAliases": ["condev@microsoft.com", "duhowett@microsoft.com"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.93.240607003" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<package id="Microsoft.Debugging.Tools.PdbStr" version="20220617.1556.0" targetFramework="native" />
|
||||
<package id="Microsoft.Debugging.Tools.SrcTool" version="20220617.1556.0" targetFramework="native" />
|
||||
|
||||
@@ -28,7 +28,7 @@ extends:
|
||||
official: true
|
||||
branding: Canary
|
||||
buildTerminal: true
|
||||
pgoBuildMode: Optimize
|
||||
pgoBuildMode: None # BODGY - OneBranch is on VS 17.10, which is known to be the worst
|
||||
codeSign: true
|
||||
signingIdentity:
|
||||
serviceName: $(SigningServiceName)
|
||||
|
||||
@@ -27,7 +27,7 @@ parameters:
|
||||
- name: pgoBuildMode
|
||||
displayName: "PGO Build Mode"
|
||||
type: string
|
||||
default: Optimize
|
||||
default: None # BODGY - OneBranch is on VS 17.10, which is known to be the worst
|
||||
values:
|
||||
- Optimize
|
||||
- Instrument
|
||||
|
||||
@@ -59,10 +59,7 @@ jobs:
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: steps-setup-versioning.yml
|
||||
|
||||
- template: steps-download-bin-dir-artifact.yml
|
||||
parameters:
|
||||
|
||||
@@ -159,10 +159,10 @@ jobs:
|
||||
.\build\scripts\Generate-ThirdPartyNotices.ps1 -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
|
||||
displayName: Generate NOTICE.html from NOTICE.md
|
||||
|
||||
- template: .\steps-restore-nuget.yml
|
||||
|
||||
- template: .\steps-install-vcpkg.yml
|
||||
|
||||
- template: .\steps-restore-nuget.yml
|
||||
|
||||
- pwsh: |-
|
||||
.\build\scripts\Set-LatestVCToolsVersion.ps1
|
||||
displayName: Work around DD-1541167 (VCToolsVersion)
|
||||
|
||||
@@ -10,6 +10,6 @@ jobs:
|
||||
submodules: false
|
||||
clean: true
|
||||
|
||||
- powershell: |-
|
||||
- pwsh: |-
|
||||
.\build\scripts\Invoke-FormattingCheck.ps1
|
||||
displayName: 'Run formatters'
|
||||
|
||||
@@ -69,10 +69,9 @@ jobs:
|
||||
fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
|
||||
- template: steps-setup-versioning.yml
|
||||
|
||||
- template: steps-download-bin-dir-artifact.yml
|
||||
parameters:
|
||||
buildPlatforms: ${{ parameters.buildPlatforms }}
|
||||
|
||||
@@ -57,10 +57,7 @@ jobs:
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: steps-setup-versioning.yml
|
||||
|
||||
- template: steps-download-bin-dir-artifact.yml
|
||||
parameters:
|
||||
|
||||
@@ -44,10 +44,7 @@ jobs:
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: steps-setup-versioning.yml
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download all PDBs from all prior build phases
|
||||
|
||||
@@ -43,10 +43,7 @@ jobs:
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: steps-setup-versioning.yml
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download all PDBs from all prior build phases
|
||||
|
||||
@@ -38,10 +38,7 @@ jobs:
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: steps-setup-versioning.yml
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download MSIX Bundle Artifact
|
||||
|
||||
@@ -92,10 +92,7 @@ stages:
|
||||
generateSbom: ${{ parameters.generateSbom }}
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
beforeBuildSteps: # Right before we build, lay down the universal package and localizations
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
|
||||
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
@@ -119,10 +116,7 @@ stages:
|
||||
generateSbom: ${{ parameters.generateSbom }}
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
beforeBuildSteps:
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
|
||||
# WPF doesn't need the localizations or the universal package, but if it does... put them here.
|
||||
|
||||
- stage: Package
|
||||
|
||||
@@ -106,11 +106,6 @@ extends:
|
||||
- stage: Build
|
||||
displayName: Build
|
||||
dependsOn: []
|
||||
variables:
|
||||
# This was set by the parent build, but we need to override it to select a specific compiler version
|
||||
- template: ./build/pipelines/templates-v2/variables-onebranch-config.yml@self
|
||||
parameters:
|
||||
containerVersion: 1.0.02566.28
|
||||
jobs:
|
||||
- template: ./build/pipelines/templates-v2/job-build-project.yml@self
|
||||
parameters:
|
||||
@@ -135,10 +130,7 @@ extends:
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
beforeBuildSteps: # Right before we build, lay down the universal package and localizations
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
|
||||
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
@@ -172,10 +164,7 @@ extends:
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
beforeBuildSteps:
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
|
||||
# WPF doesn't need the localizations or the universal package, but if it does... put them here.
|
||||
|
||||
- stage: Package
|
||||
|
||||
6
build/pipelines/templates-v2/steps-setup-versioning.yml
Normal file
6
build/pipelines/templates-v2/steps-setup-versioning.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
steps:
|
||||
- pwsh: |-
|
||||
nuget install Microsoft.Windows.Terminal.Versioning -OutputDirectory _versioning
|
||||
$VersionRoot = (Get-Item _versioning\Microsoft.Windows.*).FullName
|
||||
& "$VersionRoot\build\Setup.ps1" -ProjectDirectory "$(Build.SourcesDirectory)" -Verbose
|
||||
displayName: Set up versioning via M.W.T.V
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
|
||||
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="$(WinGetPackageRoot)\lib\Microsoft.Management.Deployment.winmd">
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<Target Name="_FixWinGetWinmdPackaging" BeforeTargets="_ComputeAppxPackagePayload">
|
||||
<ItemGroup>
|
||||
<PackagingOutputs Include="$(WinGetPackageRoot)\lib\Microsoft.Management.Deployment.winmd">
|
||||
<OutputGroup>CustomOutputGroupForPackaging</OutputGroup>
|
||||
<ProjectName>$(ProjectName)</ProjectName>
|
||||
<TargetPath>Microsoft.Management.Deployment.winmd</TargetPath>
|
||||
</PackagingOutputs>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2024</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>22</VersionMinor>
|
||||
<VersionMinor>23</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -104,6 +104,7 @@ GetConsoleKeyboardLayoutNameW(
|
||||
#define CONSOLE_REGISTRY_DEFAULTFOREGROUND L"DefaultForeground"
|
||||
#define CONSOLE_REGISTRY_DEFAULTBACKGROUND L"DefaultBackground"
|
||||
#define CONSOLE_REGISTRY_TERMINALSCROLLING L"TerminalScrolling"
|
||||
#define CONSOLE_REGISTRY_ANSWERBACKMESSAGE L"AnswerbackMessage"
|
||||
// end V2 console settings
|
||||
|
||||
/*
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
<packages>
|
||||
<!-- Native packages -->
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.93.240607003" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.7.230706001" targetFramework="native" />
|
||||
<package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240122.1" targetFramework="native" developmentDependency="true" />
|
||||
<package id="Microsoft.WindowsPackageManager.ComInterop" version="1.8.1911" targetFramework="native" developmentDependency="true" />
|
||||
|
||||
<!-- Managed packages -->
|
||||
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net45" />
|
||||
@@ -19,7 +20,7 @@
|
||||
<package id="Selenium.WebDriver" version="3.5.0" targetFramework="net45" />
|
||||
|
||||
<!-- MSBuildCache -->
|
||||
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.273-preview" />
|
||||
<package id="Microsoft.MSBuildCache.Local" version="0.1.273-preview" />
|
||||
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.273-preview" />
|
||||
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.283-preview" />
|
||||
<package id="Microsoft.MSBuildCache.Local" version="0.1.283-preview" />
|
||||
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.283-preview" />
|
||||
</packages>
|
||||
|
||||
@@ -618,6 +618,11 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "This will override the profile's `elevate` setting."
|
||||
},
|
||||
"reloadEnvironmentVariables": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "When set to true, a new environment block will be generated when creating a new session. Otherwise, the session will inherit the variables the Terminal was started with."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -2335,7 +2340,7 @@
|
||||
"description": "When set to `true`, the terminal window will auto-center itself on the display it opens on. The terminal will use the \"initialPosition\" to determine which display to open on.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"inputServiceWarning": {
|
||||
"warning.inputService": {
|
||||
"default": true,
|
||||
"description": "Warning if 'Touch Keyboard and Handwriting Panel Service' is disabled.",
|
||||
"type": "boolean"
|
||||
@@ -2350,11 +2355,21 @@
|
||||
"description": "When set to true, the terminal will focus the pane on mouse hover.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"compatibility.allowHeadless": {
|
||||
"default": false,
|
||||
"description": "When set to true, Windows Terminal will run in the background. This allows globalSummon and quakeMode actions to work even when no windows are open.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"compatibility.isolatedMode": {
|
||||
"default": false,
|
||||
"description": "When set to true, Terminal windows will not be able to interact with each other (including global hotkeys, tab drag/drop, running commandlines in existing windows, etc.). This is a compatibility escape hatch for users who are running into certain windowing issues.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"compatibility.allowDECRQCRA": {
|
||||
"default": false,
|
||||
"description": "When set to true, the terminal will support the DECRQCRA (Request Checksum of Rectangular Area) escape sequence.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyFormatting": {
|
||||
"default": true,
|
||||
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.",
|
||||
@@ -2385,12 +2400,12 @@
|
||||
"description": "When set to `true`, visual animations will be disabled across the application.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"largePasteWarning": {
|
||||
"warning.largePaste": {
|
||||
"default": true,
|
||||
"description": "When set to true, trying to paste text with more than 5 KiB of characters will display a warning asking you whether to continue or not with the paste.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"multiLinePasteWarning": {
|
||||
"warning.multiLinePaste": {
|
||||
"default": true,
|
||||
"description": "When set to true, trying to paste text with a \"new line\" character will display a warning asking you whether to continue or not with the paste.",
|
||||
"type": "boolean"
|
||||
@@ -2588,7 +2603,7 @@
|
||||
"description": "Determines the delimiters used in a double click selection.",
|
||||
"type": "string"
|
||||
},
|
||||
"confirmCloseAllTabs": {
|
||||
"warning.confirmCloseAllTabs": {
|
||||
"default": true,
|
||||
"description": "When set to \"true\" closing a window with multiple tabs open will require confirmation. When set to \"false\", the confirmation dialog will not appear.",
|
||||
"type": "boolean"
|
||||
@@ -2850,7 +2865,7 @@
|
||||
}
|
||||
},
|
||||
"autoMarkPrompts": {
|
||||
"default": false,
|
||||
"default": true,
|
||||
"description": "When set to true, prompts will automatically be marked.",
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -3032,6 +3047,10 @@
|
||||
"description": "By default Windows treats Ctrl+Alt as an alias for AltGr. When altGrAliasing is set to false, this behavior will be disabled.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"answerbackMessage": {
|
||||
"description": "The response that is sent when an ENQ control character is received.",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"description": "Stores the name of the profile generator that originated this profile.",
|
||||
"type": [
|
||||
|
||||
840
doc/specs/#13000 - In-process ConPTY.md
Normal file
840
doc/specs/#13000 - In-process ConPTY.md
Normal file
@@ -0,0 +1,840 @@
|
||||
---
|
||||
author: Leonard Hecker @lhecker
|
||||
created on: 2024-06-07
|
||||
last updated: 2024-06-07
|
||||
issue id: 13000
|
||||
---
|
||||
|
||||
# In-process ConPTY
|
||||
|
||||
## tl;dr
|
||||
|
||||
**Why?**
|
||||
* Out-of-process leads to out-of-sync issues.
|
||||
* Not all Console APIs have a VT equivalent.
|
||||
* Time consuming maintenance work due to poor encapsulation inside conhost.
|
||||
|
||||
**How?**
|
||||
1. Remove `VtEngine` and translate Console API calls directly to VT.
|
||||
2. Move all Console API related code from the `Host` to the `Server` project.
|
||||
Narrow the `IApiRoutines` interface down to its essentials.
|
||||
Replace relevant singletons with instances.
|
||||
3. Make `Server` a standalone library with `IApiRoutines` as its primary callback interface.
|
||||
Integrate the library in Windows Terminal.
|
||||
4. (Long term goal:) Ship the `Server` library as part of Windows. Build conhost itself on top of it.
|
||||
|
||||
## Why?
|
||||
|
||||
**Before:**
|
||||
|
||||
```mermaid
|
||||
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
|
||||
flowchart TD
|
||||
subgraph conhost
|
||||
direction LR
|
||||
|
||||
subgraph Server
|
||||
server_io[Console API calls]
|
||||
end
|
||||
|
||||
subgraph ConPTY
|
||||
VtIo
|
||||
VtEngine
|
||||
ConPTY_output[ConPTY Output]
|
||||
end
|
||||
|
||||
conhost_parser[VT parser]
|
||||
conhost_buffer[Text Buffer]
|
||||
conhost_renderer[Render Thread]
|
||||
conhost_atlas[Text Renderer]
|
||||
|
||||
server_io-->conhost_parser
|
||||
server_io--scroll, clear, etc.-->VtIo
|
||||
conhost_parser--resize, focus-->VtIo
|
||||
VtIo--scroll, clear, etc.-->VtEngine
|
||||
conhost_parser--unknown escape sequences-->VtEngine
|
||||
conhost_buffer--manual flushing-->VtEngine
|
||||
conhost_renderer-->VtEngine
|
||||
VtEngine-->ConPTY_output
|
||||
conhost_buffer--dirty rects-->conhost_renderer
|
||||
conhost_parser--dirty rects-->conhost_renderer
|
||||
conhost_parser--plain text-->conhost_buffer
|
||||
conhost_renderer-->conhost_atlas
|
||||
conhost_buffer--ReadBuffer-->server_io
|
||||
end
|
||||
|
||||
subgraph wt[Windows Terminal]
|
||||
direction LR
|
||||
|
||||
input[ConPTY Input]
|
||||
parser[VT parser]
|
||||
buffer[Text Buffer]
|
||||
renderer[Render Thread]
|
||||
atlas[Text Renderer]
|
||||
|
||||
input-->parser
|
||||
parser-->buffer
|
||||
buffer-->renderer
|
||||
renderer-->atlas
|
||||
parser-->renderer
|
||||
end
|
||||
|
||||
conhost--ConPTY's text pipe across processes-->wt
|
||||
```
|
||||
|
||||
**After:**
|
||||
|
||||
```mermaid
|
||||
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
|
||||
flowchart LR
|
||||
subgraph ConPTY_lib["ConPTY (static library)"]
|
||||
server_io[Console API calls]
|
||||
ConPTY_Translation[VT Translation]
|
||||
|
||||
server_io<-->ConPTY_Translation
|
||||
end
|
||||
|
||||
subgraph ConPTY["ConPTY (dynamic library)"]
|
||||
ConPTY_impl[IApiRoutines impl]
|
||||
ConPTY_parser[VT parser]
|
||||
ConPTY_buffer[Text Buffer]
|
||||
ConPTY_output[ConPTY Output]
|
||||
|
||||
ConPTY_Translation<-->ConPTY_impl
|
||||
ConPTY_impl-->ConPTY_parser
|
||||
ConPTY_parser-->ConPTY_buffer
|
||||
ConPTY_buffer--ReadBuffer-->ConPTY_impl
|
||||
ConPTY_impl------>ConPTY_output
|
||||
end
|
||||
|
||||
subgraph conhost
|
||||
conhost_impl[IApiRoutines impl]
|
||||
conhost_parser[VT parser]
|
||||
conhost_buffer[Text Buffer]
|
||||
conhost_renderer[Render Thread]
|
||||
conhost_atlas[Text Renderer]
|
||||
|
||||
ConPTY_Translation<-->conhost_impl
|
||||
conhost_impl-->conhost_parser
|
||||
conhost_parser-->conhost_buffer
|
||||
conhost_buffer--->conhost_renderer
|
||||
conhost_buffer--ReadBuffer-->conhost_impl
|
||||
conhost_renderer-->conhost_atlas
|
||||
end
|
||||
|
||||
subgraph wt[Windows Terminal]
|
||||
impl[IApiRoutines impl]
|
||||
parser[VT parser]
|
||||
buffer[Text Buffer]
|
||||
renderer[Render Thread]
|
||||
atlas[Text Renderer]
|
||||
|
||||
ConPTY_Translation<-->impl
|
||||
impl-->parser
|
||||
parser-->buffer
|
||||
buffer--->renderer
|
||||
buffer--ReadBuffer-->impl
|
||||
renderer-->atlas
|
||||
end
|
||||
```
|
||||
|
||||
To extend on the [tl;dr](#tldr):
|
||||
* ConPTY runs outside the hosting terminal which leads to an unsolvable issue: The buffer contents between ConPTY and the terminal can go out of sync.
|
||||
* The terminal and ConPTY may implement escape sequences differently.
|
||||
* ...may implement text processing differently.
|
||||
* ...may implement text reflow (resize) differently.
|
||||
* Resizing the terminal and ConPTY is asynchronous and there may be concurrent text output.
|
||||
* ...and it may uncover text from the scrollback, which ConPTY doesn't know about.
|
||||
* Some Console API methods cannot be represented via escape sequences and so ConPTY cannot produce them either.
|
||||
The most basic example of this is the lack of LVB gridlines.
|
||||
* The above suggested new architecture represents a significant simplification with no loss in features.
|
||||
* ConPTY has fulfilled our needs a thousand times over, but as it's layered on top of conhost it has resulted in software decay.
|
||||
The layer boundary has blurred over the years resulting in debugging and maintenance difficulties.
|
||||
Lastly, its performance is insufficient and has been subject to much debate.
|
||||
It's on us now to pay our debt and clean up the architecture so that ConPTY, conhost, and Windows Terminal can be built on top of it.
|
||||
|
||||
Considerations:
|
||||
* A named `MUTEX` can theoretically solve parts of the out-of-sync issue, because it could be used to synchronize buffer reflow.
|
||||
However, this requires the lock to be acquired on every API call, on top of the regular console lock, which raises ABBA deadlock concerns.
|
||||
Making this setup not just hopefully but also provably robust, is likely to be very difficult.
|
||||
It also doesn't solve any of the other listed problems.
|
||||
|
||||
## Goal 1: Remove VtEngine
|
||||
|
||||
### Goal
|
||||
|
||||
```mermaid
|
||||
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
|
||||
flowchart TD
|
||||
subgraph conhost
|
||||
direction LR
|
||||
|
||||
subgraph Server
|
||||
server_io[Console API calls]
|
||||
end
|
||||
|
||||
subgraph ConPTY
|
||||
VtIo
|
||||
VtEngine
|
||||
ConPTY_output[ConPTY Output]
|
||||
end
|
||||
|
||||
conhost_parser[VT parser]
|
||||
conhost_buffer[Text Buffer]
|
||||
conhost_renderer[Render Thread]
|
||||
conhost_atlas[Text Renderer]
|
||||
|
||||
server_io-->conhost_parser
|
||||
server_io--VT translation-->VtIo
|
||||
server_io-->VtIo
|
||||
conhost_parser-->VtIo
|
||||
VtIo-->VtEngine
|
||||
conhost_parser-->VtEngine
|
||||
conhost_buffer-->VtEngine
|
||||
conhost_renderer-->VtEngine
|
||||
VtIo-->ConPTY_output
|
||||
VtEngine-->ConPTY_output
|
||||
conhost_buffer--dirty rects-->conhost_renderer
|
||||
conhost_parser-->conhost_renderer
|
||||
conhost_parser--plain text-->conhost_buffer
|
||||
conhost_renderer-->conhost_atlas
|
||||
conhost_buffer--ReadBuffer-->server_io
|
||||
end
|
||||
|
||||
subgraph wt[Windows Terminal]
|
||||
direction LR
|
||||
|
||||
input[ConPTY Input]
|
||||
parser[VT parser]
|
||||
buffer[Text Buffer]
|
||||
renderer[Render Thread]
|
||||
atlas[Text Renderer]
|
||||
|
||||
input-->parser
|
||||
parser-->buffer
|
||||
buffer-->renderer
|
||||
renderer-->atlas
|
||||
parser-->renderer
|
||||
end
|
||||
|
||||
conhost--ConPTY's text pipe across processes-->wt
|
||||
|
||||
linkStyle 2,3,4,5,6,7,9,11 opacity:0.2
|
||||
style VtEngine opacity:0.2
|
||||
```
|
||||
|
||||
### Goals
|
||||
|
||||
* Remove VtEngine
|
||||
* Remove `--vtmode`
|
||||
* Remove `--resizeQuirk`
|
||||
* Add buffering to `VtIo` and hook it up to the output pipe
|
||||
* Implement direct VT translations for all relevant Console APIs
|
||||
|
||||
### Discussion
|
||||
|
||||
The idea is that we can translate Console API calls directly to VT at least as well as the current VtEngine setup can.
|
||||
For instance, a call to `SetConsoleCursorPosition` clearly translates directly to a `CUP` escape sequence.
|
||||
Effectively, instead of translating output asynchronously in the renderer thread, we'll do it synchronously right during the Console API call.
|
||||
|
||||
Apart from the Console APIs, the "cooked read" implementation, which handles our builtin "readline"-like text editor, will need to receive some larger changes as well.
|
||||
Its popups use `ReadConsoleOutput` and `WriteConsoleOutput` to backup and restore the affected rectangle.
|
||||
It also directly interfaces with the backing text buffer and its translation to VT relies on the existence of VtEngine.
|
||||
This results in all of the same issues that were previously outlined.
|
||||
In order to solve this, the popups need to be rewritten to use escape sequences so that they can be directly passed to the hosting terminal.
|
||||
They should also always be below the current prompt line so that we don't need to perform a potentially lossy backup/restore operation.
|
||||
|
||||
The `--vtmode xterm-ascii` switch exists for the telnet client as it only supports ASCII as per RFC854 section "THE NVT PRINTER AND KEYBOARD".
|
||||
However, telnet is the only user of this flag and it's trivial to do there (for instance by stripping high codepoints in `WriteOutputToClient` in `telnet/console.cpp`), so there's no reason for us to keep this logic in the new code.
|
||||
|
||||
This change will result in a significant reduction in complexity of our architecture.
|
||||
VT input from the shell or other clients will be given 1:1 to the hosting terminal, which will resolve our ordering and buffering issues.
|
||||
|
||||
## Goal 2: Move Console API implementations to Server
|
||||
|
||||
### Goal
|
||||
|
||||
```mermaid
|
||||
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
|
||||
flowchart TD
|
||||
subgraph conhost
|
||||
direction LR
|
||||
|
||||
subgraph Server[Server w/ ConPTY]
|
||||
server_io[Console API calls]
|
||||
ConPTY_Translation[VT Translation]
|
||||
|
||||
server_io<-->ConPTY_Translation
|
||||
end
|
||||
|
||||
subgraph ConPTY
|
||||
VtIo
|
||||
ConPTY_output[ConPTY Output]
|
||||
|
||||
VtIo-->ConPTY_output
|
||||
end
|
||||
|
||||
conhost_impl[IApiRoutines impl]
|
||||
conhost_parser[VT parser]
|
||||
conhost_buffer[Text Buffer]
|
||||
conhost_renderer[Render Thread]
|
||||
conhost_atlas[Text Renderer]
|
||||
output_new[ConPTY Output]
|
||||
|
||||
ConPTY_Translation<-->conhost_impl
|
||||
conhost_impl-->conhost_parser
|
||||
conhost_impl----->output_new
|
||||
server_io---->VtIo
|
||||
server_io-->conhost_parser
|
||||
conhost_parser-->conhost_buffer
|
||||
conhost_parser-->conhost_renderer
|
||||
conhost_buffer-->conhost_renderer
|
||||
conhost_buffer--ReadBuffer-->conhost_impl
|
||||
conhost_buffer-->server_io
|
||||
conhost_renderer-->conhost_atlas
|
||||
end
|
||||
|
||||
subgraph wt[Windows Terminal]
|
||||
direction LR
|
||||
|
||||
input[ConPTY Input]
|
||||
parser[VT parser]
|
||||
buffer[Text Buffer]
|
||||
renderer[Render Thread]
|
||||
atlas[Text Renderer]
|
||||
|
||||
input-->parser
|
||||
parser-->buffer
|
||||
buffer-->renderer
|
||||
renderer-->atlas
|
||||
parser-->renderer
|
||||
end
|
||||
|
||||
conhost--ConPTY's text pipe across processes-->wt
|
||||
|
||||
linkStyle 1,5,6,11 opacity:0.2
|
||||
style ConPTY opacity:0.2
|
||||
style VtIo opacity:0.2
|
||||
style ConPTY_output opacity:0.2
|
||||
```
|
||||
|
||||
### Goals
|
||||
|
||||
* Move the API implementation from the `Host` to the `Server` project.
|
||||
* Narrow down the `IApiRoutines` interface to its essentials.
|
||||
* Replace affected singletons with regular class instances.
|
||||
|
||||
### Discussion
|
||||
|
||||
The basic idea is that instead of having 3 arrows going in and out of the Server component, we got exactly 1.
|
||||
This makes the console server and its VT translation a reusable component, which we need so that we can solve the out-of-sync issues by integrating it into Windows Terminal.
|
||||
To make the Server API convenient to use, the interface needs to be narrowed down to as few methods as possible. This interface is currently called `IApiRoutines`.
|
||||
|
||||
### API Design
|
||||
|
||||
Design goals:
|
||||
* Low overhead<br>
|
||||
The API should never be the reason why the terminal is slow.
|
||||
Among others, the design includes both UTF8 and UTF16 functions, as both encodings are common on Windows and the server component should not make assumptions which one the terminal prefers.
|
||||
* Easy to use<br>
|
||||
The Console API is unfortunately quite powerful.
|
||||
In order to make implementing the callback interface still reasonably easy, the API should have as few methods as are needed.
|
||||
If a complex Console API call can be expressed as a series of simpler ones, and if no other performance expectations exist, it should be expressed via the simpler ones.
|
||||
* Works even if only partially implemented<br>
|
||||
Example: `CreateConsoleScreenBuffer` is seldomly used, but its existence adds significant complexity to the callback API design and potential implementations.
|
||||
A terminal should either be able to return `E_NOTIMPL` and we provide a fallback, or we provide guidance for how reasonable fallbacks can be implemented (e.g. by using the xterm alt buffer in this example).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The following API design is a rough draft just to convey the general idea.
|
||||
> It does not represent a complete, finished design.
|
||||
> This document will be updated once work on the API begins and the actual API requirements become clearer.
|
||||
|
||||
```cs
|
||||
// The Console API is built around a freely addressable frame buffer. It allows you to
|
||||
// address any part of the buffer, even those outside of what's considered the "viewport":
|
||||
//
|
||||
// ┌──────────────────┐
|
||||
// │y=-3 │
|
||||
// │ │ ╮
|
||||
// │ │ │
|
||||
// ╭ ├──────────────────┤ ├── VT scrollback (partially addressed)
|
||||
// │ │y=0 │ │
|
||||
// │ │ │ ╮ ╯
|
||||
// │ │ │ │
|
||||
// Console Buffer ──┤ ├──────────────────┤ ├──── VT Viewport (top y = 1)
|
||||
// │ │y=3 │ │
|
||||
// │ │ │ ╯
|
||||
// │ │ │
|
||||
// ╰ └──────────────────┘
|
||||
//
|
||||
// The good news is that nothing prevents you from giving the Console Buffer the exact
|
||||
// same size as the VT Viewport and for modern terminals doing so is recommended.
|
||||
// That way, coordinates are viewport-relative and content below/above the viewport is never addressed:
|
||||
//
|
||||
// ┌──────────────────┐ ╮
|
||||
// │y=-3 │ │
|
||||
// │ │ ├── VT scrollback (unused)
|
||||
// │ │ │
|
||||
// ╭ ├──────────────────┤ ╮ ╯
|
||||
// │ │y=0 │ │
|
||||
// Console Buffer ──┤ │ │ ├──── VT Viewport
|
||||
// │ │ │ │
|
||||
// ╰ └──────────────────┘ ╯
|
||||
//
|
||||
// Coordinates are 0-indexed. Note that while INT32 coordinates are used by this API, coordinates below
|
||||
// 0 and above 65535 are generally invalid as the Console ABI currently uses unsigned 16-Bit integers.
|
||||
|
||||
struct CONSRV_POINT_I32 {
|
||||
INT32 x;
|
||||
INT32 y;
|
||||
};
|
||||
|
||||
struct CONSRV_POINT_F32 {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
// These flags are also defined via Windows.h.
|
||||
#if 0
|
||||
// These flags are equivalent to the classic 4-bit indexed colors in VT via SGR.
|
||||
// However, the position of the blue and red bits are swapped.
|
||||
#define FOREGROUND_BLUE 0x0001 // Text color contains blue.
|
||||
#define FOREGROUND_GREEN 0x0002 // Text color contains green.
|
||||
#define FOREGROUND_RED 0x0004 // Text color contains red.
|
||||
#define FOREGROUND_INTENSITY 0x0008 // Text color is intensified.
|
||||
#define BACKGROUND_BLUE 0x0010 // Background color contains blue.
|
||||
#define BACKGROUND_GREEN 0x0020 // Background color contains green.
|
||||
#define BACKGROUND_RED 0x0040 // Background color contains red.
|
||||
#define BACKGROUND_INTENSITY 0x0080 // Background color is intensified.
|
||||
|
||||
// These two bits are used to represent wide glyphs.
|
||||
#define COMMON_LVB_LEADING_BYTE 0x0100 // Leading byte.
|
||||
#define COMMON_LVB_TRAILING_BYTE 0x0200 // Trailing byte.
|
||||
|
||||
// This bit is equivalent to the "CSI 7 m" reverse video escape sequence.
|
||||
#define COMMON_LVB_REVERSE_VIDEO 0x4000 // Reverse foreground and background attribute.
|
||||
|
||||
// NOTE: These flags have no equivalent in VT. COMMON_LVB_UNDERSCORE in particular is not the same as a
|
||||
// "CSI 4 m" underline in VT, despite the name. They're used to give a cell border (= grid) lines.
|
||||
#define COMMON_LVB_GRID_HORIZONTAL 0x0400 // Top horizontal.
|
||||
#define COMMON_LVB_GRID_LVERTICAL 0x0800 // Left vertical.
|
||||
#define COMMON_LVB_GRID_RVERTICAL 0x1000 // Right vertical.
|
||||
#define COMMON_LVB_UNDERSCORE 0x8000 // Underscore.
|
||||
#endif
|
||||
|
||||
// This struct is binary compatible to the CHAR_INFO struct from the Windows API and functionally equivalent.
|
||||
//
|
||||
// The following rules MUST be followed:
|
||||
// * Each instance represents 1 column in the terminal.
|
||||
// * Any cells that aren't representable with a single UCS-2 character, or are wider than 2 columns,
|
||||
// must be replaced with U+FFFD. Grapheme clusters, surrogate pairs, and similar, are not allowed.
|
||||
// Keep in mind that U+FFFD is a narrow character. It does not get the wide wide-glyph treatment below.
|
||||
// * Colors that cannot be represented via the `attributes` flags can be replaced with an approximation.
|
||||
// Alternatively, `FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED` can be used (no `BACKGROUND` bit set).
|
||||
// * If a wide glyph (2 columns wide) is encountered, the following applies:
|
||||
// * Create 2 consecutive CONSRV_CHAR_INFO instances (as per the first rule).
|
||||
// * Repeat the same `character` in both instances, even if it's U+FFFD.
|
||||
// * Assign the first instance an `attributes` flag of `COMMON_LVB_LEADING_BYTE`
|
||||
// and the second one `COMMON_LVB_TRAILING_BYTE`.
|
||||
// * BUT if the request for a CONSRV_CHAR_INFO only partially intersects a wide glyph, replace the character
|
||||
// (and only the character) with U+0020 whitespace. This also means that `COMMON_LVB_LEADING/TRAILING_BYTE`
|
||||
// should not be set, because the returned character isn't wide anymore. An example:
|
||||
// If you have a red "猫" on top of a blue background in the buffer and a single `CONSRV_CHAR_INFO` is requested
|
||||
// for the left half of the glyph, then you should set `character` to whitespace and `attributes` to
|
||||
// `FOREGROUND_RED | BACKGROUND_BLUE`. Debug builds of ConPTY will assert that you do this.
|
||||
//
|
||||
// For more documentation about the `attributes` flags, see the constants defined above.
|
||||
struct CONSRV_CHAR_INFO {
|
||||
wchar_t character;
|
||||
UINT16 attributes;
|
||||
};
|
||||
|
||||
struct CONSRV_UTF8_STRING {
|
||||
[size_is(length)] const char* data;
|
||||
DWORD length;
|
||||
};
|
||||
|
||||
struct CONSRV_UTF16_STRING {
|
||||
[size_is(length)] const wchar_t* data;
|
||||
DWORD length;
|
||||
};
|
||||
|
||||
// NOTE: At the time of writing the required fields are not fully known.
|
||||
// NOTE: boolean is 8 Bits large.
|
||||
struct CONSRV_INFO {
|
||||
// NOTE: msys2 relies on the HWND value to uniquely identify terminal sessions.
|
||||
// If we were to hand out the multiplexed terminal window HWND to msys2, it will break. Either we need to
|
||||
// create fake windows inside conhost (very bad & buggy) or break msys2 intentionally (also very bad).
|
||||
HWND window;
|
||||
CONSRV_UTF16_STRING originalWindowTitle;
|
||||
CONSRV_UTF16_STRING windowTitle;
|
||||
|
||||
CONSRV_POINT_I32 bufferSizeInCells;
|
||||
CONSRV_POINT_I32 cursorPositionInCells;
|
||||
CONSRV_POINT_I32 viewPositionInCells;
|
||||
CONSRV_POINT_I32 viewSizeInCells;
|
||||
CONSRV_POINT_F32 cellSizeInDIP;
|
||||
COLORREF colorTable[16];
|
||||
|
||||
CONSRV_POINT_I32 selectionStart;
|
||||
CONSRV_POINT_I32 selectionEnd;
|
||||
boolean selectionActive;
|
||||
boolean selectionRectangular;
|
||||
boolean selectionMouseDown;
|
||||
|
||||
float cursorHeight;
|
||||
boolean cursorHidden;
|
||||
};
|
||||
|
||||
// Any item that has changed relative to the current CONSRV_INFO will be set to a non-null pointer.
|
||||
// In other words, members that are null represent those that remain unchanged.
|
||||
//
|
||||
// If the request cannot be supported return E_INVALIDARG. For instance, you may choose to
|
||||
// do so if you receive a `bufferSizeInCells` change while the xterm alt buffer is active.
|
||||
//
|
||||
// NOTE: At the time of writing the required fields are not fully known.
|
||||
struct CONSRV_INFO_CHANGE {
|
||||
CONSRV_POINT_I32* bufferSizeInCells;
|
||||
CONSRV_POINT_I32* cursorPositionInCells;
|
||||
CONSRV_POINT_I32* viewPositionInCells;
|
||||
CONSRV_POINT_I32* viewSizeInCells;
|
||||
COLORREF* colorTable; // The referenced array is always 16 items large.
|
||||
|
||||
float* cursorHeight;
|
||||
boolean* cursorHidden;
|
||||
|
||||
CONSRV_UTF16_STRING* fontName;
|
||||
UINT32* fontFamily;
|
||||
UINT32* fontWeight;
|
||||
CONSRV_POINT_I32* fontSize;
|
||||
};
|
||||
|
||||
interface IConsoleServer : IUnknown {
|
||||
// TODO: This interface is incomplete. Among others, a way to launch new application into the server is missing.
|
||||
|
||||
// ConPTY manages stdin as a ring buffer for you. When the terminal has focus, you simply need to write your input.
|
||||
// Keyboard input MUST be written via `WriteInputRecords`. The other 2 functions DO NOT parse any VT sequences.
|
||||
// They're instead meant either for VT responses (DECRPM, etc.) and for dumping plain text (clipboard, etc.).
|
||||
void WriteInputRecords([in] DWORD count, [in, length_is(count)] const INPUT_RECORD* records);
|
||||
void WriteInputUTF8([in] CONSRV_UTF8_STRING text);
|
||||
void WriteInputUTF16([in] CONSRV_UTF16_STRING text);
|
||||
};
|
||||
|
||||
// First of all: You don't need to implement all functions and all structs perfectly for ConPTY to work decently well.
|
||||
// For instance, if you don't implement `CONSRV_INFO::cursorHeight` properly, barely anything will happen.
|
||||
interface IConsoleServerCallback : IUnknown {
|
||||
// The console server is single-threaded and no two calls will be made simultaneously. These two functions
|
||||
// simply allow you to synchronize the calls down below if your application is multi-threaded.
|
||||
//
|
||||
// Lock() will always be called before any of the functions below are called.
|
||||
// Lock() and Unlock() do not need to support recursive locking.
|
||||
// Any other calls between Lock() and Unlock() should be treated as an atomic operation.
|
||||
//
|
||||
// It is recommended to use a fair lock instead of OS primitives like SRWLOCK. These callback functions may be
|
||||
// much more often than your text renderer, etc., runs. An unfair lock will result in thread starvation.
|
||||
HRESULT Lock();
|
||||
HRESULT Unlock();
|
||||
|
||||
// If called, you're requested to create a new console alt buffer. The Console API supports having
|
||||
// multiple concurrent such buffers. They're not the same as the xterm alt buffer, however (CSI ? 1049 h):
|
||||
// They can be resized to be larger than the current viewport and switching between such buffers DOES NOT
|
||||
// clear them nor does it reset any other per-buffer state.
|
||||
//
|
||||
// If you have trouble adding support for multiple console alt buffers, consider using the xterm alt buffer
|
||||
// (CSI ? 1049 h) for the first buffer that gets created, and return E_OUTOFMEMORY for any further buffers.
|
||||
HRESULT CreateBuffer([out] void** buffer);
|
||||
|
||||
// ReleaseBuffer is called once the buffer isn't needed anymore. It's guaranteed to be called and it's guaranteed
|
||||
// to be called after ActivateBuffer() was used to switch to another buffer (or the main buffer).
|
||||
HRESULT ReleaseBuffer([in] void* buffer);
|
||||
|
||||
// This switches between different console alt buffers. Switching to a buffer should change the content that's
|
||||
// being shown, similar to the xterm alt buffer, however unlike it doing so DOES NOT reset any per-buffer state.
|
||||
// All it does is to basically swap out the underlying, active text buffer of the terminal.
|
||||
//
|
||||
// If `buffer` is NULL it's a request to switch back to the main buffer.
|
||||
//
|
||||
// `temporary` is a hint. If it's `true` it indicates that the previous buffer will soon be activated again.
|
||||
// In other words, on Unlock() the buffer was active during Lock() will be active again.
|
||||
// It's recommended that temporary switches are lightweight as they may occur relatively often.
|
||||
HRESULT ActivateBuffer([in] void* buffer, [in] boolean temporary);
|
||||
|
||||
//
|
||||
// Any functions past this point operate on the currently active buffer.
|
||||
//
|
||||
|
||||
// This function gets a snapshot of the terminal and buffer state.
|
||||
//
|
||||
// You must ensure that the returned pointer stays valid until the next GetInfo() or Unlock() call,
|
||||
// or until the currently active buffer is released.
|
||||
// You don't need to return a new instance on each call. ConPTY will only use the last returned pointer.
|
||||
// You don't need to keep the CONSRV_INFO struct constantly up to date, but you're allowed to do so.
|
||||
// For instance, it's valid to change the `.cursorPosition` when SetCursorPosition() is called.
|
||||
//
|
||||
// It's recommended that this function is lightweight as it may be called relatively often.
|
||||
const CONSRV_INFO* GetInfo();
|
||||
|
||||
// When this method is called you're asked to apply any non-null member of the given CONSRV_INFO_CHANGE struct
|
||||
// to the active buffer. For instance a non-null `.cursorPosition` is identical to calling `SetCursorPosition`,
|
||||
// a non-null `.bufferSize` is a request to resize the terminal, and so on.
|
||||
HRESULT SetInfo([in] const CONSRV_INFO_CHANGE* info);
|
||||
|
||||
// As explained in the CONSRV_POINT_I32 documentation, ConPTY coordinates may be outside of the VT viewport.
|
||||
// This function is necessary in order to support this. If you assign the console buffer the same size
|
||||
// as the VT viewport, `pos` can be translated to VT using
|
||||
// printf("\x1b[%d;%dC", pos.y + 1, pos.x + 1);
|
||||
HRESULT SetCursorPosition([in] CONSRV_POINT_I32 pos);
|
||||
|
||||
// The Console API supports 4 gridline attributes which cannot be translated to VT.
|
||||
// This function is necessary to represent those. If you don't plan to support the gridlines,
|
||||
// you can translate the attributes to VT with the following code or some equivalent:
|
||||
// static const uint8_t lut[] = { 30, 34, 32, 36, 31, 35, 33, 37, 90, 94, 92, 96, 91, 95, 93, 97 };
|
||||
// const auto fg = lut[attributes & 0xf];
|
||||
// const auto bg = lut[(attributes >> 4) & 0xf] + 10;
|
||||
// printf("\x1b[%d;%dm", fg, bg);
|
||||
//
|
||||
// `attributes` of exactly `FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED` are often used to indicate the
|
||||
// default colors in Windows Console applications, and so you may choose to translate attributes like that as:
|
||||
// printf("\x1b[39;49");
|
||||
//
|
||||
// You may also choose to support COMMON_LVB_REVERSE_VIDEO, which translates to:
|
||||
// printf("\x1b[7m");
|
||||
HRESULT SetCurrentAttributes([in] UINT16 attributes);
|
||||
|
||||
// Starting from column `pos.x` in row `pos.y`, this reads `count`-many characters and attributes.
|
||||
// `pos` and `count` will be clamped such that reads never extend outside of the `CONSRV_INFO::bufferSize`.
|
||||
//
|
||||
// However, it may still read cells that have never been written to (for instance below the current viewport!).
|
||||
// Such reads should not fail. Simply fill the `infos` array with whitespaces and a default attribute of your chosing,
|
||||
// but `FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED` is recommended (no other bits set).
|
||||
//
|
||||
// NOTE that this API should ignore any line renditions (DECDWL, DECDHL), margins (DECSLRM, ...), etc.
|
||||
// Reading outside of the "valid" range for a given row should behave exactly like reading below the viewport,
|
||||
// as described in the previous paragraph.
|
||||
HRESULT ReadBuffer([in] CONSRV_POINT_I32 pos, [in] INT32 count, [out, length_is(count)] CONSRV_CHAR_INFO* infos);
|
||||
|
||||
// These two functions are used to layout text for the internal "GNU Readline"-like implementation.
|
||||
// `text` is the string to operate on. As with any other method, input validation should be performed.
|
||||
// It's preferred to pretend as if invalid codepoints (in particular invalid surrogate pairs)
|
||||
// are U+FFFD, because this provides the user with some level of text editing capability.
|
||||
// The alternative is to have none at all when facing invalid strings which is strictly worse.
|
||||
// `maxClusters` is the maximum amount of "cursor movements" these functions should apply
|
||||
// (like when pressing the left/right arrow buttons).
|
||||
// `maxColumns` is the maximum amount of columns the functions may iterate over. When the text is "a猫" and
|
||||
// `maxColumns` is 2, then the result should be "a", because "猫" doesn't fit anymore.
|
||||
// `position` on input contains the current position of the cursor inside `text`, counted in characters from the
|
||||
// start of the `text`. On output it's supposed to contain the new cursor position.
|
||||
// `position` may be out of bounds and you should clamp it to a valid range first.
|
||||
// `columns` on output should contain the number of columns that have been iterated over.
|
||||
//
|
||||
// The idea is that a `maxClusters = 1` and `maxColumns = inf` can be used to implement left/right cursor movement,
|
||||
// while `maxClusters = inf` and `maxColumns = window width` can be used to layout text within the window.
|
||||
//
|
||||
// You don't need to handle escape characters. These functions will never be called with any present.
|
||||
// For robustness against bugs it's however recommended to handle them anyway, in whatever way you wish.
|
||||
// If you have no preference, it's recommended to treat them as zero-width characters.
|
||||
HRESULT MeasureTextForward([in] CONSRV_UTF16_STRING text, [in] DWORD maxClusters, [in] DWORD maxColumns, [in, out] DWORD* position, [out] DWORD* columns);
|
||||
HRESULT MeasureTextBackward([in] CONSRV_UTF16_STRING text, [in] DWORD maxClusters, [in] DWORD maxColumns, [in, out] DWORD* position, [out] DWORD* columns);
|
||||
|
||||
// UTF8 and UTF16 are both widely used text encodings on Windows and it's recommended that both
|
||||
// functions are reasonably fast. ConPTY will translate all non-Unicode text to UTF16 for you.
|
||||
// You must validate incoming text. It's recommended to replace invalid codepoints with U+FFFD.
|
||||
// You don't need to check for broken up codepoints at the start/end of the text, as ConPTY will handle that for you.
|
||||
HRESULT WriteUTF8([in] boolean raw, [in] CONSRV_UTF8_STRING text);
|
||||
HRESULT WriteUTF16([in] boolean raw, [in] CONSRV_UTF16_STRING text);
|
||||
};
|
||||
```
|
||||
|
||||
The list shows how each Console API function is implemented in terms of the above interface.
|
||||
* Aliases
|
||||
<br>Fully implemented inside the server component without API.
|
||||
* `AddConsoleAlias`
|
||||
* `GetConsoleAlias`
|
||||
* `GetConsoleAliases`
|
||||
* `GetConsoleAliasesLength`
|
||||
* `GetConsoleAliasExes`
|
||||
* `GetConsoleAliasExesLength`
|
||||
* History
|
||||
<br>Fully implemented inside the server component without API.
|
||||
* `ExpungeConsoleCommandHistory`
|
||||
* `GetConsoleCommandHistory`
|
||||
* `GetConsoleCommandHistoryLength`
|
||||
* `GetConsoleHistoryInfo`
|
||||
* `SetConsoleHistoryInfo`
|
||||
* `SetConsoleNumberOfCommands`
|
||||
* stdin
|
||||
<br>Fully implemented inside the server component without API.
|
||||
* `FlushConsoleInputBuffer`
|
||||
* `GetConsoleInput`
|
||||
* `GetConsoleInputCodePage`
|
||||
* `GetConsoleInputMode`
|
||||
* `GetNumberOfConsoleInputEvents`
|
||||
* `ReadConsole`
|
||||
* `SetConsoleInputCodePage`
|
||||
* `SetConsoleInputMode`
|
||||
* `WriteConsoleInput`
|
||||
* Unsupported since conhost v1
|
||||
* `GetConsoleDisplayMode`
|
||||
* `GetConsoleLangId`
|
||||
* `SetConsoleDisplayMode`
|
||||
* Buffer management
|
||||
* `GetConsoleScreenBufferInfoEx`:
|
||||
Gets information from server's internal `SCREEN_INFORMATION` class (which represents the `HANDLE`).
|
||||
* `SetConsoleScreenBufferInfoEx`:
|
||||
Sets information on server's internal `SCREEN_INFORMATION` class.
|
||||
* `CreateConsoleScreenBuffer`:
|
||||
`CreateBuffer`
|
||||
* `SetConsoleActiveScreenBuffer`:
|
||||
`ActivateBuffer` + `SetInfo`
|
||||
* `SetConsoleScreenBufferSize`:
|
||||
`SetInfo`
|
||||
* Cursor
|
||||
* `GetConsoleCursorInfo`:
|
||||
`GetInfo`
|
||||
* `SetConsoleCursorInfo`:
|
||||
`SetInfo`
|
||||
* `SetConsoleCursorPosition`:
|
||||
`GetInfo`
|
||||
* Fonts
|
||||
* `GetConsoleFontSize`:
|
||||
`GetInfo`
|
||||
* `GetCurrentConsoleFontEx`:
|
||||
`GetInfo`
|
||||
* `SetCurrentConsoleFontEx`:
|
||||
`SetInfo`
|
||||
* Window management
|
||||
* `GetConsoleOriginalTitle`:
|
||||
`GetInfo`
|
||||
* `GetConsoleSelectionInfo`:
|
||||
`GetInfo`
|
||||
* `GetConsoleTitle`:
|
||||
`GetInfo`
|
||||
* `GetConsoleWindow`:
|
||||
`GetInfo`
|
||||
* `GetLargestConsoleWindowSize`:
|
||||
`GetInfo`; The window frame size can be inferred from the difference between the `GetWindowRect(hwnd)` and the `viewSizeInCells * cellSizeInDIP`.
|
||||
The max. cell count can then be calculated by getting the `MonitorFromWindow(hwnd)` size, subtracting the frame size and calculating the cell count.
|
||||
* `GetNumberOfConsoleMouseButtons`:
|
||||
Implemented inside the server component via `GetSystemMetrics(SM_CMOUSEBUTTONS)`
|
||||
* `SetConsoleTitle`:
|
||||
`SetInfo`
|
||||
* `SetConsoleWindowInfo`:
|
||||
`SetInfo`
|
||||
* stdout (writing)
|
||||
* `FillConsoleOutputAttribute`:
|
||||
Set the new attributes with `SetCurrentAttributes`.
|
||||
For each line, get the existing contents with `ReadBuffer`, `SetCursorPosition` to the start, concatenate the cells and write them with `WriteUTF16`.
|
||||
* `FillConsoleOutputCharacter`:
|
||||
For each line, get the existing contents with `ReadBuffer`, `SetCursorPosition` to the start, concatenate the cells and write them with `WriteUTF16`.
|
||||
At the start of each line and every time the attributes change use `SetCurrentAttributes` to set them up.
|
||||
* `GetConsoleOutputCodePage`:
|
||||
Implemented inside the server component.
|
||||
* `GetConsoleOutputMode`:
|
||||
Implemented inside the server component.
|
||||
* `ScrollConsoleScreenBuffer`:
|
||||
**TODO**: It may be necessary to add a `ScrollBuffer` API to make vertical scrolling across the entire buffer width faster. This fast pass currently exists as well.
|
||||
Otherwise, this will be translated to: Read all lines in the source rectangle with `ReadBuffer`.
|
||||
Then refer to the `WriteConsoleOutput` implementation for writing it to the target.
|
||||
* `SetConsoleOutputCodePage`:
|
||||
Implemented inside the server component.
|
||||
* `SetConsoleOutputMode`:
|
||||
Implemented inside the server component.
|
||||
* `SetConsoleTextAttribute`:
|
||||
`SetCurrentAttributes`
|
||||
* `WriteConsole`:
|
||||
`WriteUTF8` if `CP_UTF8` is active and otherwise `WriteUTF16`.
|
||||
* `WriteConsoleOutput`:
|
||||
For each line, `SetCursorPosition` to the start, concatenate the cells and write them with `WriteUTF16`.
|
||||
At the start of each line and every time the attributes change use `SetCurrentAttributes` to set them up.
|
||||
* `WriteConsoleOutputAttribute`:
|
||||
Same as `FillConsoleOutputAttribute`, but with varying attributes.
|
||||
* `WriteConsoleOutputCharacter`:
|
||||
Same as `FillConsoleOutputCharacter`, but with varying characters.
|
||||
* stdout (reading)
|
||||
<br>Each of these will be translated to a series of `ReadBuffer` calls, one for each line.
|
||||
* `ReadConsoleOutput`
|
||||
* `ReadConsoleOutputAttribute`
|
||||
* `ReadConsoleOutputCharacter`
|
||||
|
||||
## Goal 3: Productize Server
|
||||
|
||||
### Goal
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph ConPTY_lib["ConPTY (static library)"]
|
||||
server_io[Console API calls]
|
||||
ConPTY_Translation[VT Translation]
|
||||
|
||||
server_io<-->ConPTY_Translation
|
||||
end
|
||||
|
||||
subgraph ConPTY["ConPTY (dynamic library)"]
|
||||
ConPTY_impl[IApiRoutines impl]
|
||||
ConPTY_parser[VT parser]
|
||||
ConPTY_buffer[Text Buffer]
|
||||
ConPTY_output[ConPTY Output]
|
||||
|
||||
ConPTY_Translation<-->ConPTY_impl
|
||||
ConPTY_impl-->ConPTY_parser
|
||||
ConPTY_parser-->ConPTY_buffer
|
||||
ConPTY_buffer--ReadBuffer-->ConPTY_impl
|
||||
ConPTY_impl------>ConPTY_output
|
||||
end
|
||||
|
||||
subgraph conhost
|
||||
subgraph conhost_Server[Server]
|
||||
conhost_server_io[Console API calls]
|
||||
conhost_ConPTY_Translation[VT Translation]
|
||||
|
||||
conhost_server_io<-->conhost_ConPTY_Translation
|
||||
end
|
||||
|
||||
conhost_impl[IApiRoutines impl]
|
||||
conhost_parser[VT parser]
|
||||
conhost_buffer[Text Buffer]
|
||||
conhost_renderer[Render Thread]
|
||||
conhost_atlas[Text Renderer]
|
||||
output_new[ConPTY Output]
|
||||
|
||||
conhost_ConPTY_Translation<-->conhost_impl
|
||||
conhost_impl-.remains for<br>back compat.....->output_new
|
||||
|
||||
ConPTY_Translation<-->conhost_impl
|
||||
conhost_impl-->conhost_parser
|
||||
conhost_parser-->conhost_buffer
|
||||
conhost_buffer--->conhost_renderer
|
||||
conhost_buffer--ReadBuffer-->conhost_impl
|
||||
conhost_renderer-->conhost_atlas
|
||||
end
|
||||
|
||||
subgraph wt[Windows Terminal]
|
||||
impl[IApiRoutines impl]
|
||||
parser[VT parser]
|
||||
buffer[Text Buffer]
|
||||
renderer[Render Thread]
|
||||
atlas[Text Renderer]
|
||||
|
||||
ConPTY_Translation<-->impl
|
||||
impl-->parser
|
||||
parser-->buffer
|
||||
buffer--->renderer
|
||||
buffer--ReadBuffer-->impl
|
||||
renderer-->atlas
|
||||
end
|
||||
|
||||
linkStyle 6,7 opacity:0.2
|
||||
style conhost_Server opacity:0.2
|
||||
style conhost_server_io opacity:0.2
|
||||
style conhost_ConPTY_Translation opacity:0.2
|
||||
```
|
||||
|
||||
After creating an _internal_ API in conhost, the next logical step is for us to extract the interface as a public one and consume it in Windows Terminal.
|
||||
At this point we should also see if we can find early adopters of the API in other projects.
|
||||
|
||||
## Stretch Goal 4: Ship Server in Windows
|
||||
|
||||
The console server on Windows has been an internal ABI for the longest time.
|
||||
As we have started shipping ConPTY bundled with conhost/OpenConsole to other projects, the internal ABI has become a public one.
|
||||
While we have no intention of ever breaking forward compatability of the console server with future Windows versions, it would be reassuring if the ABI long-term became internal once more.
|
||||
This will allow us complete freedom over its design, even in the decades to come, and allow us to fully shim any 3rd party applications on Windows if a ABI change were to ever happen.
|
||||
27
oss/chromium/LICENSE
Normal file
27
oss/chromium/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * 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.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "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 COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS 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.
|
||||
17
oss/chromium/MAINTAINER_README.md
Normal file
17
oss/chromium/MAINTAINER_README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
### Notes for Future Maintainers
|
||||
|
||||
This was originally imported by @miniksa in January 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
1. Go to chromium/chromium repository on GitHub.
|
||||
2. Take the entire contents of the base/numerics directory wholesale and drop it in the base/numerics directory here.
|
||||
3. Don't change anything about it.
|
||||
4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme.
|
||||
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
|
||||
5. Submit the pull.
|
||||
|
||||
28
oss/chromium/base/numerics/BUILD.gn
Normal file
28
oss/chromium/base/numerics/BUILD.gn
Normal file
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2017 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
# This is a dependency-free, header-only, library, and it needs to stay that
|
||||
# way to facilitate pulling it into various third-party projects. So, this
|
||||
# file is here to protect against accidentally introducing external
|
||||
# dependencies or depending on internal implementation details.
|
||||
source_set("base_numerics") {
|
||||
visibility = [ "//base/*" ]
|
||||
sources = [
|
||||
"checked_math_impl.h",
|
||||
"clamped_math_impl.h",
|
||||
"safe_conversions_arm_impl.h",
|
||||
"safe_conversions_impl.h",
|
||||
"safe_math_arm_impl.h",
|
||||
"safe_math_clang_gcc_impl.h",
|
||||
"safe_math_shared_impl.h",
|
||||
]
|
||||
public = [
|
||||
"checked_math.h",
|
||||
"clamped_math.h",
|
||||
"math_constants.h",
|
||||
"ranges.h",
|
||||
"safe_conversions.h",
|
||||
"safe_math.h",
|
||||
]
|
||||
}
|
||||
7
oss/chromium/base/numerics/DEPS
Normal file
7
oss/chromium/base/numerics/DEPS
Normal file
@@ -0,0 +1,7 @@
|
||||
# This is a dependency-free, header-only, library, and it needs to stay that
|
||||
# way to facilitate pulling it into various third-party projects. So, this
|
||||
# file is here to protect against accidentally introducing dependencies.
|
||||
include_rules = [
|
||||
"-base",
|
||||
"+base/numerics",
|
||||
]
|
||||
5
oss/chromium/base/numerics/OWNERS
Normal file
5
oss/chromium/base/numerics/OWNERS
Normal file
@@ -0,0 +1,5 @@
|
||||
jschuh@chromium.org
|
||||
tsepez@chromium.org
|
||||
|
||||
|
||||
# COMPONENT: Internals
|
||||
409
oss/chromium/base/numerics/README.md
Normal file
409
oss/chromium/base/numerics/README.md
Normal file
@@ -0,0 +1,409 @@
|
||||
# `base/numerics`
|
||||
|
||||
This directory contains a dependency-free, header-only library of templates
|
||||
providing well-defined semantics for safely and performantly handling a variety
|
||||
of numeric operations, including most common arithmetic operations and
|
||||
conversions.
|
||||
|
||||
The public API is broken out into the following header files:
|
||||
|
||||
* `checked_math.h` contains the `CheckedNumeric` template class and helper
|
||||
functions for performing arithmetic and conversion operations that detect
|
||||
errors and boundary conditions (e.g. overflow, truncation, etc.).
|
||||
* `clamped_math.h` contains the `ClampedNumeric` template class and
|
||||
helper functions for performing fast, clamped (i.e. [non-sticky](#notsticky)
|
||||
saturating) arithmetic operations and conversions.
|
||||
* `safe_conversions.h` contains the `StrictNumeric` template class and
|
||||
a collection of custom casting templates and helper functions for safely
|
||||
converting between a range of numeric types.
|
||||
* `safe_math.h` includes all of the previously mentioned headers.
|
||||
|
||||
*** aside
|
||||
**Note:** The `Numeric` template types implicitly convert from C numeric types
|
||||
and `Numeric` templates that are convertable to an underlying C numeric type.
|
||||
The conversion priority for `Numeric` type coercions is:
|
||||
|
||||
* `StrictNumeric` coerces to `ClampedNumeric` and `CheckedNumeric`
|
||||
* `ClampedNumeric` coerces to `CheckedNumeric`
|
||||
***
|
||||
|
||||
[TOC]
|
||||
|
||||
## Common patterns and use-cases
|
||||
|
||||
The following covers the preferred style for the most common uses of this
|
||||
library. Please don't cargo-cult from anywhere else. 😉
|
||||
|
||||
### Performing checked arithmetic type conversions
|
||||
|
||||
The `checked_cast` template converts between arbitrary arithmetic types, and is
|
||||
used for cases where a conversion failure should result in program termination:
|
||||
|
||||
```cpp
|
||||
// Crash if signed_value is out of range for buff_size.
|
||||
size_t buff_size = checked_cast<size_t>(signed_value);
|
||||
```
|
||||
|
||||
### Performing saturated (clamped) arithmetic type conversions
|
||||
|
||||
The `saturated_cast` template converts between arbitrary arithmetic types, and
|
||||
is used in cases where an out-of-bounds source value should be saturated to the
|
||||
corresponding maximum or minimum of the destination type:
|
||||
|
||||
```cpp
|
||||
// Convert from float with saturation to INT_MAX, INT_MIN, or 0 for NaN.
|
||||
int int_value = saturated_cast<int>(floating_point_value);
|
||||
```
|
||||
|
||||
### Enforcing arithmetic type conversions at compile-time
|
||||
|
||||
The `strict_cast` emits code that is identical to `static_cast`. However,
|
||||
provides static checks that will cause a compilation failure if the
|
||||
destination type cannot represent the full range of the source type:
|
||||
|
||||
```cpp
|
||||
// Throw a compiler error if byte_value is changed to an out-of-range-type.
|
||||
int int_value = strict_cast<int>(byte_value);
|
||||
```
|
||||
|
||||
You can also enforce these compile-time restrictions on function parameters by
|
||||
using the `StrictNumeric` template:
|
||||
|
||||
```cpp
|
||||
// Throw a compiler error if the size argument cannot be represented by a
|
||||
// size_t (e.g. passing an int will fail to compile).
|
||||
bool AllocateBuffer(void** buffer, StrictCast<size_t> size);
|
||||
```
|
||||
|
||||
### Comparing values between arbitrary arithmetic types
|
||||
|
||||
Both the `StrictNumeric` and `ClampedNumeric` types provide well defined
|
||||
comparisons between arbitrary arithmetic types. This allows you to perform
|
||||
comparisons that are not legal or would trigger compiler warnings or errors
|
||||
under the normal arithmetic promotion rules:
|
||||
|
||||
```cpp
|
||||
bool foo(unsigned value, int upper_bound) {
|
||||
// Converting to StrictNumeric allows this comparison to work correctly.
|
||||
if (MakeStrictNum(value) >= upper_bound)
|
||||
return false;
|
||||
```
|
||||
|
||||
*** note
|
||||
**Warning:** Do not perform manual conversions using the comparison operators.
|
||||
Instead, use the cast templates described in the previous sections, or the
|
||||
constexpr template functions `IsValueInRangeForNumericType` and
|
||||
`IsTypeInRangeForNumericType`, as these templates properly handle the full range
|
||||
of corner cases and employ various optimizations.
|
||||
***
|
||||
|
||||
### Calculating a buffer size (checked arithmetic)
|
||||
|
||||
When making exact calculations—such as for buffer lengths—it's often necessary
|
||||
to know when those calculations trigger an overflow, undefined behavior, or
|
||||
other boundary conditions. The `CheckedNumeric` template does this by storing
|
||||
a bit determining whether or not some arithmetic operation has occured that
|
||||
would put the variable in an "invalid" state. Attempting to extract the value
|
||||
from a variable in an invalid state will trigger a check/trap condition, that
|
||||
by default will result in process termination.
|
||||
|
||||
Here's an example of a buffer calculation using a `CheckedNumeric` type (note:
|
||||
the AssignIfValid method will trigger a compile error if the result is ignored).
|
||||
|
||||
```cpp
|
||||
// Calculate the buffer size and detect if an overflow occurs.
|
||||
size_t size;
|
||||
if (!CheckAdd(kHeaderSize, CheckMul(count, kItemSize)).AssignIfValid(&size)) {
|
||||
// Handle an overflow error...
|
||||
}
|
||||
```
|
||||
|
||||
### Calculating clamped coordinates (non-sticky saturating arithmetic)
|
||||
|
||||
Certain classes of calculations—such as coordinate calculations—require
|
||||
well-defined semantics that always produce a valid result on boundary
|
||||
conditions. The `ClampedNumeric` template addresses this by providing
|
||||
performant, non-sticky saturating arithmetic operations.
|
||||
|
||||
Here's an example of using a `ClampedNumeric` to calculate an operation
|
||||
insetting a rectangle.
|
||||
|
||||
```cpp
|
||||
// Use clamped arithmetic since inset calculations might overflow.
|
||||
void Rect::Inset(int left, int top, int right, int bottom) {
|
||||
origin_ += Vector2d(left, top);
|
||||
set_width(ClampSub(width(), ClampAdd(left, right)));
|
||||
set_height(ClampSub(height(), ClampAdd(top, bottom)));
|
||||
}
|
||||
```
|
||||
|
||||
*** note
|
||||
<a name="notsticky"></a>
|
||||
The `ClampedNumeric` type is not "sticky", which means the saturation is not
|
||||
retained across individual operations. As such, one arithmetic operation may
|
||||
result in a saturated value, while the next operation may then "desaturate"
|
||||
the value. Here's an example:
|
||||
|
||||
```cpp
|
||||
ClampedNumeric<int> value = INT_MAX;
|
||||
++value; // value is still INT_MAX, due to saturation.
|
||||
--value; // value is now (INT_MAX - 1), because saturation is not sticky.
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Conversion functions and StrictNumeric<> in safe_conversions.h
|
||||
|
||||
This header includes a collection of helper `constexpr` templates for safely
|
||||
performing a range of conversions, assignments, and tests.
|
||||
|
||||
### Safe casting templates
|
||||
|
||||
* `as_signed()` - Returns the supplied integral value as a signed type of
|
||||
the same width.
|
||||
* `as_unsigned()` - Returns the supplied integral value as an unsigned type
|
||||
of the same width.
|
||||
* `checked_cast<>()` - Analogous to `static_cast<>` for numeric types, except
|
||||
that by default it will trigger a crash on an out-of-bounds conversion (e.g.
|
||||
overflow, underflow, NaN to integral) or a compile error if the conversion
|
||||
error can be detected at compile time. The crash handler can be overridden
|
||||
to perform a behavior other than crashing.
|
||||
* `saturated_cast<>()` - Analogous to `static_cast` for numeric types, except
|
||||
that it returns a saturated result when the specified numeric conversion
|
||||
would otherwise overflow or underflow. An NaN source returns 0 by
|
||||
default, but can be overridden to return a different result.
|
||||
* `strict_cast<>()` - Analogous to `static_cast` for numeric types, except
|
||||
this causes a compile failure if the destination type is not large
|
||||
enough to contain any value in the source type. It performs no runtime
|
||||
checking and thus introduces no runtime overhead.
|
||||
|
||||
### Other helper and conversion functions
|
||||
|
||||
* `IsValueInRangeForNumericType<>()` - A convenience function that returns
|
||||
true if the type supplied as the template parameter can represent the value
|
||||
passed as an argument to the function.
|
||||
* `IsTypeInRangeForNumericType<>()` - A convenience function that evaluates
|
||||
entirely at compile-time and returns true if the destination type (first
|
||||
template parameter) can represent the full range of the source type
|
||||
(second template parameter).
|
||||
* `IsValueNegative()` - A convenience function that will accept any
|
||||
arithmetic type as an argument and will return whether the value is less
|
||||
than zero. Unsigned types always return false.
|
||||
* `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer
|
||||
parameter as an unsigned result (thus avoiding an overflow if the value
|
||||
is the signed, two's complement minimum).
|
||||
|
||||
### StrictNumeric<>
|
||||
|
||||
`StrictNumeric<>` is a wrapper type that performs assignments and copies via
|
||||
the `strict_cast` template, and can perform valid arithmetic comparisons
|
||||
across any range of arithmetic types. `StrictNumeric` is the return type for
|
||||
values extracted from a `CheckedNumeric` class instance. The raw numeric value
|
||||
is extracted via `static_cast` to the underlying type or any type with
|
||||
sufficient range to represent the underlying type.
|
||||
|
||||
* `MakeStrictNum()` - Creates a new `StrictNumeric` from the underlying type
|
||||
of the supplied arithmetic or StrictNumeric type.
|
||||
* `SizeT` - Alias for `StrictNumeric<size_t>`.
|
||||
|
||||
## CheckedNumeric<> in checked_math.h
|
||||
|
||||
`CheckedNumeric<>` implements all the logic and operators for detecting integer
|
||||
boundary conditions such as overflow, underflow, and invalid conversions.
|
||||
The `CheckedNumeric` type implicitly converts from floating point and integer
|
||||
data types, and contains overloads for basic arithmetic operations (i.e.: `+`,
|
||||
`-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers).
|
||||
However, *the [variadic template functions
|
||||
](#CheckedNumeric_in-checked_math_h-Non_member-helper-functions)
|
||||
are the prefered API,* as they remove type ambiguities and help prevent a number
|
||||
of common errors. The variadic functions can also be more performant, as they
|
||||
eliminate redundant expressions that are unavoidable with the with the operator
|
||||
overloads. (Ideally the compiler should optimize those away, but better to avoid
|
||||
them in the first place.)
|
||||
|
||||
Type promotions are a slightly modified version of the [standard C/C++ numeric
|
||||
promotions
|
||||
](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
|
||||
with the two differences being that *there is no default promotion to int*
|
||||
and *bitwise logical operations always return an unsigned of the wider type.*
|
||||
|
||||
### Members
|
||||
|
||||
The unary negation, increment, and decrement operators are supported, along
|
||||
with the following unary arithmetic methods, which return a new
|
||||
`CheckedNumeric` as a result of the operation:
|
||||
|
||||
* `Abs()` - Absolute value.
|
||||
* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
|
||||
(valid for only integral types).
|
||||
* `Max()` - Returns whichever is greater of the current instance or argument.
|
||||
The underlying return type is whichever has the greatest magnitude.
|
||||
* `Min()` - Returns whichever is lowest of the current instance or argument.
|
||||
The underlying return type is whichever has can represent the lowest
|
||||
number in the smallest width (e.g. int8_t over unsigned, int over
|
||||
int8_t, and float over int).
|
||||
|
||||
The following are for converting `CheckedNumeric` instances:
|
||||
|
||||
* `type` - The underlying numeric type.
|
||||
* `AssignIfValid()` - Assigns the underlying value to the supplied
|
||||
destination pointer if the value is currently valid and within the
|
||||
range supported by the destination type. Returns true on success.
|
||||
* `Cast<>()` - Instance method returning a `CheckedNumeric` derived from
|
||||
casting the current instance to a `CheckedNumeric` of the supplied
|
||||
destination type.
|
||||
|
||||
*** aside
|
||||
The following member functions return a `StrictNumeric`, which is valid for
|
||||
comparison and assignment operations, but will trigger a compile failure on
|
||||
attempts to assign to a type of insufficient range. The underlying value can
|
||||
be extracted by an explicit `static_cast` to the underlying type or any type
|
||||
with sufficient range to represent the underlying type.
|
||||
***
|
||||
|
||||
* `IsValid()` - Returns true if the underlying numeric value is valid (i.e.
|
||||
has not wrapped or saturated and is not the result of an invalid
|
||||
conversion).
|
||||
* `ValueOrDie()` - Returns the underlying value. If the state is not valid
|
||||
this call will trigger a crash by default (but may be overridden by
|
||||
supplying an alternate handler to the template).
|
||||
* `ValueOrDefault()` - Returns the current value, or the supplied default if
|
||||
the state is not valid (but will not crash).
|
||||
|
||||
**Comparison operators are explicitly not provided** for `CheckedNumeric`
|
||||
types because they could result in a crash if the type is not in a valid state.
|
||||
Patterns like the following should be used instead:
|
||||
|
||||
```cpp
|
||||
// Either input or padding (or both) may be arbitrary sizes.
|
||||
size_t buff_size;
|
||||
if (!CheckAdd(input, padding, kHeaderLength).AssignIfValid(&buff_size) ||
|
||||
buff_size >= kMaxBuffer) {
|
||||
// Handle an error...
|
||||
} else {
|
||||
// Do stuff on success...
|
||||
}
|
||||
```
|
||||
|
||||
### Non-member helper functions
|
||||
|
||||
The following variadic convenience functions, which accept standard arithmetic
|
||||
or `CheckedNumeric` types, perform arithmetic operations, and return a
|
||||
`CheckedNumeric` result. The supported functions are:
|
||||
|
||||
* `CheckAdd()` - Addition.
|
||||
* `CheckSub()` - Subtraction.
|
||||
* `CheckMul()` - Multiplication.
|
||||
* `CheckDiv()` - Division.
|
||||
* `CheckMod()` - Modulus (integer only).
|
||||
* `CheckLsh()` - Left integer shift (integer only).
|
||||
* `CheckRsh()` - Right integer shift (integer only).
|
||||
* `CheckAnd()` - Bitwise AND (integer only with unsigned result).
|
||||
* `CheckOr()` - Bitwise OR (integer only with unsigned result).
|
||||
* `CheckXor()` - Bitwise XOR (integer only with unsigned result).
|
||||
* `CheckMax()` - Maximum of supplied arguments.
|
||||
* `CheckMin()` - Minimum of supplied arguments.
|
||||
|
||||
The following wrapper functions can be used to avoid the template
|
||||
disambiguator syntax when converting a destination type.
|
||||
|
||||
* `IsValidForType<>()` in place of: `a.template IsValid<>()`
|
||||
* `ValueOrDieForType<>()` in place of: `a.template ValueOrDie<>()`
|
||||
* `ValueOrDefaultForType<>()` in place of: `a.template ValueOrDefault<>()`
|
||||
|
||||
The following general utility methods is are useful for converting from
|
||||
arithmetic types to `CheckedNumeric` types:
|
||||
|
||||
* `MakeCheckedNum()` - Creates a new `CheckedNumeric` from the underlying type
|
||||
of the supplied arithmetic or directly convertible type.
|
||||
|
||||
## ClampedNumeric<> in clamped_math.h
|
||||
|
||||
`ClampedNumeric<>` implements all the logic and operators for clamped
|
||||
(non-sticky saturating) arithmetic operations and conversions. The
|
||||
`ClampedNumeric` type implicitly converts back and forth between floating point
|
||||
and integer data types, saturating on assignment as appropriate. It contains
|
||||
overloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for
|
||||
all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison
|
||||
operators for arithmetic types of any size. However, *the [variadic template
|
||||
functions
|
||||
](#ClampedNumeric_in-clamped_math_h-Non_member-helper-functions)
|
||||
are the prefered API,* as they remove type ambiguities and help prevent
|
||||
a number of common errors. The variadic functions can also be more performant,
|
||||
as they eliminate redundant expressions that are unavoidable with the operator
|
||||
overloads. (Ideally the compiler should optimize those away, but better to avoid
|
||||
them in the first place.)
|
||||
|
||||
Type promotions are a slightly modified version of the [standard C/C++ numeric
|
||||
promotions
|
||||
](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
|
||||
with the two differences being that *there is no default promotion to int*
|
||||
and *bitwise logical operations always return an unsigned of the wider type.*
|
||||
|
||||
*** aside
|
||||
Most arithmetic operations saturate normally, to the numeric limit in the
|
||||
direction of the sign. The potentially unusual cases are:
|
||||
|
||||
* **Division:** Division by zero returns the saturated limit in the direction
|
||||
of sign of the dividend (first argument). The one exception is 0/0, which
|
||||
returns zero (although logically is NaN).
|
||||
* **Modulus:** Division by zero returns the dividend (first argument).
|
||||
* **Left shift:** Non-zero values saturate in the direction of the signed
|
||||
limit (max/min), even for shifts larger than the bit width. 0 shifted any
|
||||
amount results in 0.
|
||||
* **Right shift:** Negative values saturate to -1. Positive or 0 saturates
|
||||
to 0. (Effectively just an unbounded arithmetic-right-shift.)
|
||||
* **Bitwise operations:** No saturation; bit pattern is identical to
|
||||
non-saturated bitwise operations.
|
||||
***
|
||||
|
||||
### Members
|
||||
|
||||
The unary negation, increment, and decrement operators are supported, along
|
||||
with the following unary arithmetic methods, which return a new
|
||||
`ClampedNumeric` as a result of the operation:
|
||||
|
||||
* `Abs()` - Absolute value.
|
||||
* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
|
||||
(valid for only integral types).
|
||||
* `Max()` - Returns whichever is greater of the current instance or argument.
|
||||
The underlying return type is whichever has the greatest magnitude.
|
||||
* `Min()` - Returns whichever is lowest of the current instance or argument.
|
||||
The underlying return type is whichever has can represent the lowest
|
||||
number in the smallest width (e.g. int8_t over unsigned, int over
|
||||
int8_t, and float over int).
|
||||
|
||||
The following are for converting `ClampedNumeric` instances:
|
||||
|
||||
* `type` - The underlying numeric type.
|
||||
* `RawValue()` - Returns the raw value as the underlying arithmetic type. This
|
||||
is useful when e.g. assigning to an auto type or passing as a deduced
|
||||
template parameter.
|
||||
* `Cast<>()` - Instance method returning a `ClampedNumeric` derived from
|
||||
casting the current instance to a `ClampedNumeric` of the supplied
|
||||
destination type.
|
||||
|
||||
### Non-member helper functions
|
||||
|
||||
The following variadic convenience functions, which accept standard arithmetic
|
||||
or `ClampedNumeric` types, perform arithmetic operations, and return a
|
||||
`ClampedNumeric` result. The supported functions are:
|
||||
|
||||
* `ClampAdd()` - Addition.
|
||||
* `ClampSub()` - Subtraction.
|
||||
* `ClampMul()` - Multiplication.
|
||||
* `ClampDiv()` - Division.
|
||||
* `ClampMod()` - Modulus (integer only).
|
||||
* `ClampLsh()` - Left integer shift (integer only).
|
||||
* `ClampRsh()` - Right integer shift (integer only).
|
||||
* `ClampAnd()` - Bitwise AND (integer only with unsigned result).
|
||||
* `ClampOr()` - Bitwise OR (integer only with unsigned result).
|
||||
* `ClampXor()` - Bitwise XOR (integer only with unsigned result).
|
||||
* `ClampMax()` - Maximum of supplied arguments.
|
||||
* `ClampMin()` - Minimum of supplied arguments.
|
||||
|
||||
The following is a general utility method that is useful for converting
|
||||
to a `ClampedNumeric` type:
|
||||
|
||||
* `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type
|
||||
of the supplied arithmetic or directly convertible type.
|
||||
393
oss/chromium/base/numerics/checked_math.h
Normal file
393
oss/chromium/base/numerics/checked_math.h
Normal file
@@ -0,0 +1,393 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_CHECKED_MATH_H_
|
||||
#define BASE_NUMERICS_CHECKED_MATH_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/checked_math_impl.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
class CheckedNumeric {
|
||||
static_assert(std::is_arithmetic<T>::value,
|
||||
"CheckedNumeric<T>: T must be a numeric type.");
|
||||
|
||||
public:
|
||||
using type = T;
|
||||
|
||||
constexpr CheckedNumeric() = default;
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
|
||||
: state_(rhs.state_.value(), rhs.IsValid()) {}
|
||||
|
||||
template <typename Src>
|
||||
friend class CheckedNumeric;
|
||||
|
||||
// This is not an explicit constructor because we implicitly upgrade regular
|
||||
// numerics to CheckedNumerics to make them easier to use.
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
|
||||
: state_(value) {
|
||||
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||
}
|
||||
|
||||
// This is not an explicit constructor because we want a seamless conversion
|
||||
// from StrictNumeric types.
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric(
|
||||
StrictNumeric<Src> value) // NOLINT(runtime/explicit)
|
||||
: state_(static_cast<Src>(value)) {}
|
||||
|
||||
// IsValid() - The public API to test if a CheckedNumeric is currently valid.
|
||||
// A range checked destination type can be supplied using the Dst template
|
||||
// parameter.
|
||||
template <typename Dst = T>
|
||||
constexpr bool IsValid() const {
|
||||
return state_.is_valid() &&
|
||||
IsValueInRangeForNumericType<Dst>(state_.value());
|
||||
}
|
||||
|
||||
// AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
|
||||
// and is within the range supported by the destination type. Returns true if
|
||||
// successful and false otherwise.
|
||||
template <typename Dst>
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
__attribute__((warn_unused_result))
|
||||
#elif defined(_MSC_VER)
|
||||
_Check_return_
|
||||
#endif
|
||||
constexpr bool
|
||||
AssignIfValid(Dst* result) const {
|
||||
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
|
||||
? ((*result = static_cast<Dst>(state_.value())), true)
|
||||
: false;
|
||||
}
|
||||
|
||||
// ValueOrDie() - The primary accessor for the underlying value. If the
|
||||
// current state is not valid it will CHECK and crash.
|
||||
// A range checked destination type can be supplied using the Dst template
|
||||
// parameter, which will trigger a CHECK if the value is not in bounds for
|
||||
// the destination.
|
||||
// The CHECK behavior can be overridden by supplying a handler as a
|
||||
// template parameter, for test code, etc. However, the handler cannot access
|
||||
// the underlying value, and it is not available through other means.
|
||||
template <typename Dst = T, class CheckHandler = CheckOnFailure>
|
||||
constexpr StrictNumeric<Dst> ValueOrDie() const {
|
||||
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
|
||||
? static_cast<Dst>(state_.value())
|
||||
: CheckHandler::template HandleFailure<Dst>();
|
||||
}
|
||||
|
||||
// ValueOrDefault(T default_value) - A convenience method that returns the
|
||||
// current value if the state is valid, and the supplied default_value for
|
||||
// any other state.
|
||||
// A range checked destination type can be supplied using the Dst template
|
||||
// parameter. WARNING: This function may fail to compile or CHECK at runtime
|
||||
// if the supplied default_value is not within range of the destination type.
|
||||
template <typename Dst = T, typename Src>
|
||||
constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
|
||||
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
|
||||
? static_cast<Dst>(state_.value())
|
||||
: checked_cast<Dst>(default_value);
|
||||
}
|
||||
|
||||
// Returns a checked numeric of the specified type, cast from the current
|
||||
// CheckedNumeric. If the current state is invalid or the destination cannot
|
||||
// represent the result then the returned CheckedNumeric will be invalid.
|
||||
template <typename Dst>
|
||||
constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// This friend method is available solely for providing more detailed logging
|
||||
// in the the tests. Do not implement it in production code, because the
|
||||
// underlying values may change at any time.
|
||||
template <typename U>
|
||||
friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
|
||||
|
||||
// Prototypes for the supported arithmetic operator overloads.
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator+=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator-=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator*=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator/=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator%=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator<<=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator>>=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator&=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator|=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric& operator^=(const Src rhs);
|
||||
|
||||
constexpr CheckedNumeric operator-() const {
|
||||
// The negation of two's complement int min is int min, so we simply
|
||||
// check for that in the constexpr case.
|
||||
// We use an optimized code path for a known run-time variable.
|
||||
return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
|
||||
std::is_floating_point<T>::value
|
||||
? CheckedNumeric<T>(
|
||||
NegateWrapper(state_.value()),
|
||||
IsValid() && (!std::is_signed<T>::value ||
|
||||
std::is_floating_point<T>::value ||
|
||||
NegateWrapper(state_.value()) !=
|
||||
std::numeric_limits<T>::lowest()))
|
||||
: FastRuntimeNegate();
|
||||
}
|
||||
|
||||
constexpr CheckedNumeric operator~() const {
|
||||
return CheckedNumeric<decltype(InvertWrapper(T()))>(
|
||||
InvertWrapper(state_.value()), IsValid());
|
||||
}
|
||||
|
||||
constexpr CheckedNumeric Abs() const {
|
||||
return !IsValueNegative(state_.value()) ? *this : -*this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
|
||||
const U rhs) const {
|
||||
using R = typename UnderlyingType<U>::type;
|
||||
using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
|
||||
// TODO(jschuh): This can be converted to the MathOp version and remain
|
||||
// constexpr once we have C++14 support.
|
||||
return CheckedNumeric<result_type>(
|
||||
static_cast<result_type>(
|
||||
IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
|
||||
? state_.value()
|
||||
: Wrapper<U>::value(rhs)),
|
||||
state_.is_valid() && Wrapper<U>::is_valid(rhs));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
|
||||
const U rhs) const {
|
||||
using R = typename UnderlyingType<U>::type;
|
||||
using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
|
||||
// TODO(jschuh): This can be converted to the MathOp version and remain
|
||||
// constexpr once we have C++14 support.
|
||||
return CheckedNumeric<result_type>(
|
||||
static_cast<result_type>(
|
||||
IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
|
||||
? state_.value()
|
||||
: Wrapper<U>::value(rhs)),
|
||||
state_.is_valid() && Wrapper<U>::is_valid(rhs));
|
||||
}
|
||||
|
||||
// This function is available only for integral types. It returns an unsigned
|
||||
// integer of the same width as the source type, containing the absolute value
|
||||
// of the source, and properly handling signed min.
|
||||
constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
|
||||
UnsignedAbs() const {
|
||||
return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
|
||||
SafeUnsignedAbs(state_.value()), state_.is_valid());
|
||||
}
|
||||
|
||||
constexpr CheckedNumeric& operator++() {
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr CheckedNumeric operator++(int) {
|
||||
CheckedNumeric value = *this;
|
||||
*this += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
constexpr CheckedNumeric& operator--() {
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr CheckedNumeric operator--(int) {
|
||||
CheckedNumeric value = *this;
|
||||
*this -= 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
// These perform the actual math operations on the CheckedNumerics.
|
||||
// Binary arithmetic operations.
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R>
|
||||
static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
|
||||
using Math = typename MathWrapper<M, L, R>::math;
|
||||
T result = 0;
|
||||
bool is_valid =
|
||||
Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
|
||||
Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
|
||||
return CheckedNumeric<T>(result, is_valid);
|
||||
}
|
||||
|
||||
// Assignment arithmetic operations.
|
||||
template <template <typename, typename, typename> class M, typename R>
|
||||
constexpr CheckedNumeric& MathOp(const R rhs) {
|
||||
using Math = typename MathWrapper<M, T, R>::math;
|
||||
T result = 0; // Using T as the destination saves a range check.
|
||||
bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
|
||||
Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
|
||||
*this = CheckedNumeric<T>(result, is_valid);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
CheckedNumericState<T> state_;
|
||||
|
||||
CheckedNumeric FastRuntimeNegate() const {
|
||||
T result;
|
||||
bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
|
||||
return CheckedNumeric<T>(result, IsValid() && success);
|
||||
}
|
||||
|
||||
template <typename Src>
|
||||
constexpr CheckedNumeric(Src value, bool is_valid)
|
||||
: state_(value, is_valid) {}
|
||||
|
||||
// These wrappers allow us to handle state the same way for both
|
||||
// CheckedNumeric and POD arithmetic types.
|
||||
template <typename Src>
|
||||
struct Wrapper {
|
||||
static constexpr bool is_valid(Src) { return true; }
|
||||
static constexpr Src value(Src value) { return value; }
|
||||
};
|
||||
|
||||
template <typename Src>
|
||||
struct Wrapper<CheckedNumeric<Src>> {
|
||||
static constexpr bool is_valid(const CheckedNumeric<Src> v) {
|
||||
return v.IsValid();
|
||||
}
|
||||
static constexpr Src value(const CheckedNumeric<Src> v) {
|
||||
return v.state_.value();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Src>
|
||||
struct Wrapper<StrictNumeric<Src>> {
|
||||
static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
|
||||
static constexpr Src value(const StrictNumeric<Src> v) {
|
||||
return static_cast<Src>(v);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Convenience functions to avoid the ugly template disambiguator syntax.
|
||||
template <typename Dst, typename Src>
|
||||
constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
|
||||
return value.template IsValid<Dst>();
|
||||
}
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr StrictNumeric<Dst> ValueOrDieForType(
|
||||
const CheckedNumeric<Src> value) {
|
||||
return value.template ValueOrDie<Dst>();
|
||||
}
|
||||
|
||||
template <typename Dst, typename Src, typename Default>
|
||||
constexpr StrictNumeric<Dst> ValueOrDefaultForType(
|
||||
const CheckedNumeric<Src> value,
|
||||
const Default default_value) {
|
||||
return value.template ValueOrDefault<Dst>(default_value);
|
||||
}
|
||||
|
||||
// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
|
||||
// or CheckedNumericType.
|
||||
template <typename T>
|
||||
constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
|
||||
const T value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// These implement the variadic wrapper for the math operations.
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R>
|
||||
constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
|
||||
const L lhs,
|
||||
const R rhs) {
|
||||
using Math = typename MathWrapper<M, L, R>::math;
|
||||
return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
|
||||
rhs);
|
||||
}
|
||||
|
||||
// General purpose wrapper template for arithmetic operations.
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R,
|
||||
typename... Args>
|
||||
constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
|
||||
CheckMathOp(const L lhs, const R rhs, const Args... args) {
|
||||
return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
|
||||
}
|
||||
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
|
||||
BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
|
||||
BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
|
||||
|
||||
// These are some extra StrictNumeric operators to support simple pointer
|
||||
// arithmetic with our result types. Since wrapping on a pointer is always
|
||||
// bad, we trigger the CHECK condition here.
|
||||
template <typename L, typename R>
|
||||
L* operator+(L* lhs, const StrictNumeric<R> rhs) {
|
||||
uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
|
||||
CheckMul(sizeof(L), static_cast<R>(rhs)))
|
||||
.template ValueOrDie<uintptr_t>();
|
||||
return reinterpret_cast<L*>(result);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
L* operator-(L* lhs, const StrictNumeric<R> rhs) {
|
||||
uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
|
||||
CheckMul(sizeof(L), static_cast<R>(rhs)))
|
||||
.template ValueOrDie<uintptr_t>();
|
||||
return reinterpret_cast<L*>(result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
using internal::CheckedNumeric;
|
||||
using internal::IsValidForType;
|
||||
using internal::ValueOrDieForType;
|
||||
using internal::ValueOrDefaultForType;
|
||||
using internal::MakeCheckedNum;
|
||||
using internal::CheckMax;
|
||||
using internal::CheckMin;
|
||||
using internal::CheckAdd;
|
||||
using internal::CheckSub;
|
||||
using internal::CheckMul;
|
||||
using internal::CheckDiv;
|
||||
using internal::CheckMod;
|
||||
using internal::CheckLsh;
|
||||
using internal::CheckRsh;
|
||||
using internal::CheckAnd;
|
||||
using internal::CheckOr;
|
||||
using internal::CheckXor;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_CHECKED_MATH_H_
|
||||
567
oss/chromium/base/numerics/checked_math_impl.h
Normal file
567
oss/chromium/base/numerics/checked_math_impl.h
Normal file
@@ -0,0 +1,567 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_
|
||||
#define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/numerics/safe_math_shared_impl.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
constexpr bool CheckedAddImpl(T x, T y, T* result) {
|
||||
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||
// Since the value of x+y is undefined if we have a signed type, we compute
|
||||
// it using the unsigned type of the same size.
|
||||
using UnsignedDst = typename std::make_unsigned<T>::type;
|
||||
using SignedDst = typename std::make_signed<T>::type;
|
||||
UnsignedDst ux = static_cast<UnsignedDst>(x);
|
||||
UnsignedDst uy = static_cast<UnsignedDst>(y);
|
||||
UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
|
||||
*result = static_cast<T>(uresult);
|
||||
// Addition is valid if the sign of (x + y) is equal to either that of x or
|
||||
// that of y.
|
||||
return (std::is_signed<T>::value)
|
||||
? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
|
||||
: uresult >= uy; // Unsigned is either valid or underflow.
|
||||
}
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedAddOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedAddOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||
if (CheckedAddFastOp<T, U>::is_supported)
|
||||
return CheckedAddFastOp<T, U>::Do(x, y, result);
|
||||
|
||||
// Double the underlying type up to a full machine word.
|
||||
using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||
using Promotion =
|
||||
typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
|
||||
IntegerBitsPlusSign<intptr_t>::value),
|
||||
typename BigEnoughPromotion<T, U>::type,
|
||||
FastPromotion>::type;
|
||||
// Fail if either operand is out of range for the promoted type.
|
||||
// TODO(jschuh): This could be made to work for a broader range of values.
|
||||
if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||
!IsValueInRangeForNumericType<Promotion>(y))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Promotion presult = {};
|
||||
bool is_valid = true;
|
||||
if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
|
||||
presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
|
||||
} else {
|
||||
is_valid = CheckedAddImpl(static_cast<Promotion>(x),
|
||||
static_cast<Promotion>(y), &presult);
|
||||
}
|
||||
*result = static_cast<V>(presult);
|
||||
return is_valid && IsValueInRangeForNumericType<V>(presult);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool CheckedSubImpl(T x, T y, T* result) {
|
||||
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||
// Since the value of x+y is undefined if we have a signed type, we compute
|
||||
// it using the unsigned type of the same size.
|
||||
using UnsignedDst = typename std::make_unsigned<T>::type;
|
||||
using SignedDst = typename std::make_signed<T>::type;
|
||||
UnsignedDst ux = static_cast<UnsignedDst>(x);
|
||||
UnsignedDst uy = static_cast<UnsignedDst>(y);
|
||||
UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
|
||||
*result = static_cast<T>(uresult);
|
||||
// Subtraction is valid if either x and y have same sign, or (x-y) and x have
|
||||
// the same sign.
|
||||
return (std::is_signed<T>::value)
|
||||
? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
|
||||
: x >= y;
|
||||
}
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedSubOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedSubOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||
if (CheckedSubFastOp<T, U>::is_supported)
|
||||
return CheckedSubFastOp<T, U>::Do(x, y, result);
|
||||
|
||||
// Double the underlying type up to a full machine word.
|
||||
using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||
using Promotion =
|
||||
typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
|
||||
IntegerBitsPlusSign<intptr_t>::value),
|
||||
typename BigEnoughPromotion<T, U>::type,
|
||||
FastPromotion>::type;
|
||||
// Fail if either operand is out of range for the promoted type.
|
||||
// TODO(jschuh): This could be made to work for a broader range of values.
|
||||
if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||
!IsValueInRangeForNumericType<Promotion>(y))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Promotion presult = {};
|
||||
bool is_valid = true;
|
||||
if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
|
||||
presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
|
||||
} else {
|
||||
is_valid = CheckedSubImpl(static_cast<Promotion>(x),
|
||||
static_cast<Promotion>(y), &presult);
|
||||
}
|
||||
*result = static_cast<V>(presult);
|
||||
return is_valid && IsValueInRangeForNumericType<V>(presult);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool CheckedMulImpl(T x, T y, T* result) {
|
||||
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||
// Since the value of x*y is potentially undefined if we have a signed type,
|
||||
// we compute it using the unsigned type of the same size.
|
||||
using UnsignedDst = typename std::make_unsigned<T>::type;
|
||||
using SignedDst = typename std::make_signed<T>::type;
|
||||
const UnsignedDst ux = SafeUnsignedAbs(x);
|
||||
const UnsignedDst uy = SafeUnsignedAbs(y);
|
||||
UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
|
||||
const bool is_negative =
|
||||
std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
|
||||
*result = is_negative ? 0 - uresult : uresult;
|
||||
// We have a fast out for unsigned identity or zero on the second operand.
|
||||
// After that it's an unsigned overflow check on the absolute value, with
|
||||
// a +1 bound for a negative result.
|
||||
return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
|
||||
ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
|
||||
}
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedMulOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedMulOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||
if (CheckedMulFastOp<T, U>::is_supported)
|
||||
return CheckedMulFastOp<T, U>::Do(x, y, result);
|
||||
|
||||
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||
// Verify the destination type can hold the result (always true for 0).
|
||||
if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||
!IsValueInRangeForNumericType<Promotion>(y)) &&
|
||||
x && y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Promotion presult = {};
|
||||
bool is_valid = true;
|
||||
if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
|
||||
// The fast op may be available with the promoted type.
|
||||
is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(x, y, &presult);
|
||||
} else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
|
||||
presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
|
||||
} else {
|
||||
is_valid = CheckedMulImpl(static_cast<Promotion>(x),
|
||||
static_cast<Promotion>(y), &presult);
|
||||
}
|
||||
*result = static_cast<V>(presult);
|
||||
return is_valid && IsValueInRangeForNumericType<V>(presult);
|
||||
}
|
||||
};
|
||||
|
||||
// Division just requires a check for a zero denominator or an invalid negation
|
||||
// on signed min/-1.
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedDivOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedDivOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
if (BASE_NUMERICS_UNLIKELY(!y))
|
||||
return false;
|
||||
|
||||
// The overflow check can be compiled away if we don't have the exact
|
||||
// combination of types needed to trigger this case.
|
||||
using Promotion = typename BigEnoughPromotion<T, U>::type;
|
||||
if (BASE_NUMERICS_UNLIKELY(
|
||||
(std::is_signed<T>::value && std::is_signed<U>::value &&
|
||||
IsTypeInRangeForNumericType<T, Promotion>::value &&
|
||||
static_cast<Promotion>(x) ==
|
||||
std::numeric_limits<Promotion>::lowest() &&
|
||||
y == static_cast<U>(-1)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This branch always compiles away if the above branch wasn't removed.
|
||||
if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||
!IsValueInRangeForNumericType<Promotion>(y)) &&
|
||||
x)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Promotion presult = Promotion(x) / Promotion(y);
|
||||
*result = static_cast<V>(presult);
|
||||
return IsValueInRangeForNumericType<V>(presult);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedModOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedModOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
using Promotion = typename BigEnoughPromotion<T, U>::type;
|
||||
if (BASE_NUMERICS_LIKELY(y)) {
|
||||
Promotion presult = static_cast<Promotion>(x) % static_cast<Promotion>(y);
|
||||
*result = static_cast<Promotion>(presult);
|
||||
return IsValueInRangeForNumericType<V>(presult);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedLshOp {};
|
||||
|
||||
// Left shift. Shifts less than 0 or greater than or equal to the number
|
||||
// of bits in the promoted type are undefined. Shifts of negative values
|
||||
// are undefined. Otherwise it is defined when the result fits.
|
||||
template <typename T, typename U>
|
||||
struct CheckedLshOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = T;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U shift, V* result) {
|
||||
// Disallow negative numbers and verify the shift is in bounds.
|
||||
if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
|
||||
as_unsigned(shift) <
|
||||
as_unsigned(std::numeric_limits<T>::digits))) {
|
||||
// Shift as unsigned to avoid undefined behavior.
|
||||
*result = static_cast<V>(as_unsigned(x) << shift);
|
||||
// If the shift can be reversed, we know it was valid.
|
||||
return *result >> shift == x;
|
||||
}
|
||||
|
||||
// Handle the legal corner-case of a full-width signed shift of zero.
|
||||
return std::is_signed<T>::value && !x &&
|
||||
as_unsigned(shift) == as_unsigned(std::numeric_limits<T>::digits);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedRshOp {};
|
||||
|
||||
// Right shift. Shifts less than 0 or greater than or equal to the number
|
||||
// of bits in the promoted type are undefined. Otherwise, it is always defined,
|
||||
// but a right shift of a negative value is implementation-dependent.
|
||||
template <typename T, typename U>
|
||||
struct CheckedRshOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = T;
|
||||
template <typename V>
|
||||
static bool Do(T x, U shift, V* result) {
|
||||
// Use the type conversion push negative values out of range.
|
||||
if (BASE_NUMERICS_LIKELY(as_unsigned(shift) <
|
||||
IntegerBitsPlusSign<T>::value)) {
|
||||
T tmp = x >> shift;
|
||||
*result = static_cast<V>(tmp);
|
||||
return IsValueInRangeForNumericType<V>(tmp);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedAndOp {};
|
||||
|
||||
// For simplicity we support only unsigned integer results.
|
||||
template <typename T, typename U>
|
||||
struct CheckedAndOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename std::make_unsigned<
|
||||
typename MaxExponentPromotion<T, U>::type>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
|
||||
*result = static_cast<V>(tmp);
|
||||
return IsValueInRangeForNumericType<V>(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedOrOp {};
|
||||
|
||||
// For simplicity we support only unsigned integers.
|
||||
template <typename T, typename U>
|
||||
struct CheckedOrOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename std::make_unsigned<
|
||||
typename MaxExponentPromotion<T, U>::type>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
|
||||
*result = static_cast<V>(tmp);
|
||||
return IsValueInRangeForNumericType<V>(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedXorOp {};
|
||||
|
||||
// For simplicity we support only unsigned integers.
|
||||
template <typename T, typename U>
|
||||
struct CheckedXorOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename std::make_unsigned<
|
||||
typename MaxExponentPromotion<T, U>::type>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
|
||||
*result = static_cast<V>(tmp);
|
||||
return IsValueInRangeForNumericType<V>(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
// Max doesn't really need to be implemented this way because it can't fail,
|
||||
// but it makes the code much cleaner to use the MathOp wrappers.
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedMaxOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedMaxOp<
|
||||
T,
|
||||
U,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||
std::is_arithmetic<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
result_type tmp = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
|
||||
: static_cast<result_type>(y);
|
||||
*result = static_cast<V>(tmp);
|
||||
return IsValueInRangeForNumericType<V>(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
// Min doesn't really need to be implemented this way because it can't fail,
|
||||
// but it makes the code much cleaner to use the MathOp wrappers.
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct CheckedMinOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedMinOp<
|
||||
T,
|
||||
U,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||
std::is_arithmetic<U>::value>::type> {
|
||||
using result_type = typename LowestValuePromotion<T, U>::type;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T x, U y, V* result) {
|
||||
result_type tmp = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
|
||||
: static_cast<result_type>(y);
|
||||
*result = static_cast<V>(tmp);
|
||||
return IsValueInRangeForNumericType<V>(tmp);
|
||||
}
|
||||
};
|
||||
|
||||
// This is just boilerplate that wraps the standard floating point arithmetic.
|
||||
// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
|
||||
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
|
||||
template <typename T, typename U> \
|
||||
struct Checked##NAME##Op< \
|
||||
T, U, \
|
||||
typename std::enable_if<std::is_floating_point<T>::value || \
|
||||
std::is_floating_point<U>::value>::type> { \
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type; \
|
||||
template <typename V> \
|
||||
static constexpr bool Do(T x, U y, V* result) { \
|
||||
using Promotion = typename MaxExponentPromotion<T, U>::type; \
|
||||
Promotion presult = x OP y; \
|
||||
*result = static_cast<V>(presult); \
|
||||
return IsValueInRangeForNumericType<V>(presult); \
|
||||
} \
|
||||
};
|
||||
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Add, +)
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Div, /)
|
||||
|
||||
#undef BASE_FLOAT_ARITHMETIC_OPS
|
||||
|
||||
// Floats carry around their validity state with them, but integers do not. So,
|
||||
// we wrap the underlying value in a specialization in order to hide that detail
|
||||
// and expose an interface via accessors.
|
||||
enum NumericRepresentation {
|
||||
NUMERIC_INTEGER,
|
||||
NUMERIC_FLOATING,
|
||||
NUMERIC_UNKNOWN
|
||||
};
|
||||
|
||||
template <typename NumericType>
|
||||
struct GetNumericRepresentation {
|
||||
static const NumericRepresentation value =
|
||||
std::is_integral<NumericType>::value
|
||||
? NUMERIC_INTEGER
|
||||
: (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
|
||||
: NUMERIC_UNKNOWN);
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
NumericRepresentation type = GetNumericRepresentation<T>::value>
|
||||
class CheckedNumericState {};
|
||||
|
||||
// Integrals require quite a bit of additional housekeeping to manage state.
|
||||
template <typename T>
|
||||
class CheckedNumericState<T, NUMERIC_INTEGER> {
|
||||
private:
|
||||
// is_valid_ precedes value_ because member intializers in the constructors
|
||||
// are evaluated in field order, and is_valid_ must be read when initializing
|
||||
// value_.
|
||||
bool is_valid_;
|
||||
T value_;
|
||||
|
||||
// Ensures that a type conversion does not trigger undefined behavior.
|
||||
template <typename Src>
|
||||
static constexpr T WellDefinedConversionOrZero(const Src value,
|
||||
const bool is_valid) {
|
||||
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||
return (std::is_integral<SrcType>::value || is_valid)
|
||||
? static_cast<T>(value)
|
||||
: static_cast<T>(0);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename Src, NumericRepresentation type>
|
||||
friend class CheckedNumericState;
|
||||
|
||||
constexpr CheckedNumericState() : is_valid_(true), value_(0) {}
|
||||
|
||||
template <typename Src>
|
||||
constexpr CheckedNumericState(Src value, bool is_valid)
|
||||
: is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
|
||||
value_(WellDefinedConversionOrZero(value, is_valid_)) {
|
||||
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||
}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
|
||||
: is_valid_(rhs.IsValid()),
|
||||
value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
|
||||
|
||||
template <typename Src>
|
||||
constexpr explicit CheckedNumericState(Src value)
|
||||
: is_valid_(IsValueInRangeForNumericType<T>(value)),
|
||||
value_(WellDefinedConversionOrZero(value, is_valid_)) {}
|
||||
|
||||
constexpr bool is_valid() const { return is_valid_; }
|
||||
constexpr T value() const { return value_; }
|
||||
};
|
||||
|
||||
// Floating points maintain their own validity, but need translation wrappers.
|
||||
template <typename T>
|
||||
class CheckedNumericState<T, NUMERIC_FLOATING> {
|
||||
private:
|
||||
T value_;
|
||||
|
||||
// Ensures that a type conversion does not trigger undefined behavior.
|
||||
template <typename Src>
|
||||
static constexpr T WellDefinedConversionOrNaN(const Src value,
|
||||
const bool is_valid) {
|
||||
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||
return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
|
||||
NUMERIC_RANGE_CONTAINED ||
|
||||
is_valid)
|
||||
? static_cast<T>(value)
|
||||
: std::numeric_limits<T>::quiet_NaN();
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename Src, NumericRepresentation type>
|
||||
friend class CheckedNumericState;
|
||||
|
||||
constexpr CheckedNumericState() : value_(0.0) {}
|
||||
|
||||
template <typename Src>
|
||||
constexpr CheckedNumericState(Src value, bool is_valid)
|
||||
: value_(WellDefinedConversionOrNaN(value, is_valid)) {}
|
||||
|
||||
template <typename Src>
|
||||
constexpr explicit CheckedNumericState(Src value)
|
||||
: value_(WellDefinedConversionOrNaN(
|
||||
value,
|
||||
IsValueInRangeForNumericType<T>(value))) {}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
|
||||
: value_(WellDefinedConversionOrNaN(
|
||||
rhs.value(),
|
||||
rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
|
||||
|
||||
constexpr bool is_valid() const {
|
||||
// Written this way because std::isfinite is not reliably constexpr.
|
||||
return MustTreatAsConstexpr(value_)
|
||||
? value_ <= std::numeric_limits<T>::max() &&
|
||||
value_ >= std::numeric_limits<T>::lowest()
|
||||
: std::isfinite(value_);
|
||||
}
|
||||
constexpr T value() const { return value_; }
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_
|
||||
264
oss/chromium/base/numerics/clamped_math.h
Normal file
264
oss/chromium/base/numerics/clamped_math.h
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_CLAMPED_MATH_H_
|
||||
#define BASE_NUMERICS_CLAMPED_MATH_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/clamped_math_impl.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
class ClampedNumeric {
|
||||
static_assert(std::is_arithmetic<T>::value,
|
||||
"ClampedNumeric<T>: T must be a numeric type.");
|
||||
|
||||
public:
|
||||
using type = T;
|
||||
|
||||
constexpr ClampedNumeric() : value_(0) {}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
|
||||
: value_(saturated_cast<T>(rhs.value_)) {}
|
||||
|
||||
template <typename Src>
|
||||
friend class ClampedNumeric;
|
||||
|
||||
// This is not an explicit constructor because we implicitly upgrade regular
|
||||
// numerics to ClampedNumerics to make them easier to use.
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit)
|
||||
: value_(saturated_cast<T>(value)) {
|
||||
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||
}
|
||||
|
||||
// This is not an explicit constructor because we want a seamless conversion
|
||||
// from StrictNumeric types.
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric(
|
||||
StrictNumeric<Src> value) // NOLINT(runtime/explicit)
|
||||
: value_(saturated_cast<T>(static_cast<Src>(value))) {}
|
||||
|
||||
// Returns a ClampedNumeric of the specified type, cast from the current
|
||||
// ClampedNumeric, and saturated to the destination type.
|
||||
template <typename Dst>
|
||||
constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Prototypes for the supported arithmetic operator overloads.
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator+=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator-=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator*=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator/=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator%=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator<<=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator>>=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator&=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator|=(const Src rhs);
|
||||
template <typename Src>
|
||||
constexpr ClampedNumeric& operator^=(const Src rhs);
|
||||
|
||||
constexpr ClampedNumeric operator-() const {
|
||||
// The negation of two's complement int min is int min, so that's the
|
||||
// only overflow case where we will saturate.
|
||||
return ClampedNumeric<T>(SaturatedNegWrapper(value_));
|
||||
}
|
||||
|
||||
constexpr ClampedNumeric operator~() const {
|
||||
return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));
|
||||
}
|
||||
|
||||
constexpr ClampedNumeric Abs() const {
|
||||
// The negation of two's complement int min is int min, so that's the
|
||||
// only overflow case where we will saturate.
|
||||
return ClampedNumeric<T>(SaturatedAbsWrapper(value_));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
|
||||
const U rhs) const {
|
||||
using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
|
||||
return ClampedNumeric<result_type>(
|
||||
ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
|
||||
const U rhs) const {
|
||||
using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
|
||||
return ClampedNumeric<result_type>(
|
||||
ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
|
||||
}
|
||||
|
||||
// This function is available only for integral types. It returns an unsigned
|
||||
// integer of the same width as the source type, containing the absolute value
|
||||
// of the source, and properly handling signed min.
|
||||
constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>
|
||||
UnsignedAbs() const {
|
||||
return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(
|
||||
SafeUnsignedAbs(value_));
|
||||
}
|
||||
|
||||
constexpr ClampedNumeric& operator++() {
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ClampedNumeric operator++(int) {
|
||||
ClampedNumeric value = *this;
|
||||
*this += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
constexpr ClampedNumeric& operator--() {
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ClampedNumeric operator--(int) {
|
||||
ClampedNumeric value = *this;
|
||||
*this -= 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
// These perform the actual math operations on the ClampedNumerics.
|
||||
// Binary arithmetic operations.
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R>
|
||||
static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {
|
||||
using Math = typename MathWrapper<M, L, R>::math;
|
||||
return ClampedNumeric<T>(
|
||||
Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
|
||||
}
|
||||
|
||||
// Assignment arithmetic operations.
|
||||
template <template <typename, typename, typename> class M, typename R>
|
||||
constexpr ClampedNumeric& MathOp(const R rhs) {
|
||||
using Math = typename MathWrapper<M, T, R>::math;
|
||||
*this =
|
||||
ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Dst>
|
||||
constexpr operator Dst() const {
|
||||
return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
|
||||
value_);
|
||||
}
|
||||
|
||||
// This method extracts the raw integer value without saturating it to the
|
||||
// destination type as the conversion operator does. This is useful when
|
||||
// e.g. assigning to an auto type or passing as a deduced template parameter.
|
||||
constexpr T RawValue() const { return value_; }
|
||||
|
||||
private:
|
||||
T value_;
|
||||
|
||||
// These wrappers allow us to handle state the same way for both
|
||||
// ClampedNumeric and POD arithmetic types.
|
||||
template <typename Src>
|
||||
struct Wrapper {
|
||||
static constexpr Src value(Src value) {
|
||||
return static_cast<typename UnderlyingType<Src>::type>(value);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Convience wrapper to return a new ClampedNumeric from the provided arithmetic
|
||||
// or ClampedNumericType.
|
||||
template <typename T>
|
||||
constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
|
||||
const T value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||
// Overload the ostream output operator to make logging work nicely.
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
|
||||
os << static_cast<T>(value);
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
// These implement the variadic wrapper for the math operations.
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R>
|
||||
constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(
|
||||
const L lhs,
|
||||
const R rhs) {
|
||||
using Math = typename MathWrapper<M, L, R>::math;
|
||||
return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
|
||||
rhs);
|
||||
}
|
||||
|
||||
// General purpose wrapper template for arithmetic operations.
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R,
|
||||
typename... Args>
|
||||
constexpr ClampedNumeric<typename ResultType<M, L, R, Args...>::type>
|
||||
ClampMathOp(const L lhs, const R rhs, const Args... args) {
|
||||
return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
|
||||
}
|
||||
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
|
||||
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
|
||||
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=)
|
||||
|
||||
} // namespace internal
|
||||
|
||||
using internal::ClampedNumeric;
|
||||
using internal::MakeClampedNum;
|
||||
using internal::ClampMax;
|
||||
using internal::ClampMin;
|
||||
using internal::ClampAdd;
|
||||
using internal::ClampSub;
|
||||
using internal::ClampMul;
|
||||
using internal::ClampDiv;
|
||||
using internal::ClampMod;
|
||||
using internal::ClampLsh;
|
||||
using internal::ClampRsh;
|
||||
using internal::ClampAnd;
|
||||
using internal::ClampOr;
|
||||
using internal::ClampXor;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_CLAMPED_MATH_H_
|
||||
341
oss/chromium/base/numerics/clamped_math_impl.h
Normal file
341
oss/chromium/base/numerics/clamped_math_impl.h
Normal file
@@ -0,0 +1,341 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
|
||||
#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/checked_math.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/numerics/safe_math_shared_impl.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_signed<T>::value>::type* = nullptr>
|
||||
constexpr T SaturatedNegWrapper(T value) {
|
||||
return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported
|
||||
? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
|
||||
? NegateWrapper(value)
|
||||
: std::numeric_limits<T>::max())
|
||||
: ClampedNegFastOp<T>::Do(value);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
!std::is_signed<T>::value>::type* = nullptr>
|
||||
constexpr T SaturatedNegWrapper(T value) {
|
||||
return T(0);
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
constexpr T SaturatedNegWrapper(T value) {
|
||||
return -value;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
constexpr T SaturatedAbsWrapper(T value) {
|
||||
// The calculation below is a static identity for unsigned types, but for
|
||||
// signed integer types it provides a non-branching, saturated absolute value.
|
||||
// This works because SafeUnsignedAbs() returns an unsigned type, which can
|
||||
// represent the absolute value of all negative numbers of an equal-width
|
||||
// integer type. The call to IsValueNegative() then detects overflow in the
|
||||
// special case of numeric_limits<T>::min(), by evaluating the bit pattern as
|
||||
// a signed integer value. If it is the overflow case, we end up subtracting
|
||||
// one from the unsigned result, thus saturating to numeric_limits<T>::max().
|
||||
return static_cast<T>(SafeUnsignedAbs(value) -
|
||||
IsValueNegative<T>(SafeUnsignedAbs(value)));
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
constexpr T SaturatedAbsWrapper(T value) {
|
||||
return value < 0 ? -value : value;
|
||||
}
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedAddOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedAddOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U y) {
|
||||
if (ClampedAddFastOp<T, U>::is_supported)
|
||||
return ClampedAddFastOp<T, U>::template Do<V>(x, y);
|
||||
|
||||
static_assert(std::is_same<V, result_type>::value ||
|
||||
IsTypeInRangeForNumericType<U, V>::value,
|
||||
"The saturation result cannot be determined from the "
|
||||
"provided types.");
|
||||
const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
|
||||
V result = {};
|
||||
return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
|
||||
? result
|
||||
: saturated;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedSubOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedSubOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U y) {
|
||||
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||
if (ClampedSubFastOp<T, U>::is_supported)
|
||||
return ClampedSubFastOp<T, U>::template Do<V>(x, y);
|
||||
|
||||
static_assert(std::is_same<V, result_type>::value ||
|
||||
IsTypeInRangeForNumericType<U, V>::value,
|
||||
"The saturation result cannot be determined from the "
|
||||
"provided types.");
|
||||
const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
|
||||
V result = {};
|
||||
return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
|
||||
? result
|
||||
: saturated;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedMulOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedMulOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U y) {
|
||||
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||
if (ClampedMulFastOp<T, U>::is_supported)
|
||||
return ClampedMulFastOp<T, U>::template Do<V>(x, y);
|
||||
|
||||
V result = {};
|
||||
const V saturated =
|
||||
CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
|
||||
return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
|
||||
? result
|
||||
: saturated;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedDivOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedDivOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U y) {
|
||||
V result = {};
|
||||
if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
|
||||
return result;
|
||||
// Saturation goes to max, min, or NaN (if x is zero).
|
||||
return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
|
||||
: SaturationDefaultLimits<V>::NaN();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedModOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedModOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U y) {
|
||||
V result = {};
|
||||
return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
|
||||
? result
|
||||
: x;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedLshOp {};
|
||||
|
||||
// Left shift. Non-zero values saturate in the direction of the sign. A zero
|
||||
// shifted by any value always results in zero.
|
||||
template <typename T, typename U>
|
||||
struct ClampedLshOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = T;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U shift) {
|
||||
static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
|
||||
if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
|
||||
// Shift as unsigned to avoid undefined behavior.
|
||||
V result = static_cast<V>(as_unsigned(x) << shift);
|
||||
// If the shift can be reversed, we know it was valid.
|
||||
if (BASE_NUMERICS_LIKELY(result >> shift == x))
|
||||
return result;
|
||||
}
|
||||
return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedRshOp {};
|
||||
|
||||
// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
|
||||
template <typename T, typename U>
|
||||
struct ClampedRshOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = T;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U shift) {
|
||||
static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
|
||||
// Signed right shift is odd, because it saturates to -1 or 0.
|
||||
const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
|
||||
return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
|
||||
? saturated_cast<V>(x >> shift)
|
||||
: saturated;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedAndOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedAndOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename std::make_unsigned<
|
||||
typename MaxExponentPromotion<T, U>::type>::type;
|
||||
template <typename V>
|
||||
static constexpr V Do(T x, U y) {
|
||||
return static_cast<result_type>(x) & static_cast<result_type>(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedOrOp {};
|
||||
|
||||
// For simplicity we promote to unsigned integers.
|
||||
template <typename T, typename U>
|
||||
struct ClampedOrOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename std::make_unsigned<
|
||||
typename MaxExponentPromotion<T, U>::type>::type;
|
||||
template <typename V>
|
||||
static constexpr V Do(T x, U y) {
|
||||
return static_cast<result_type>(x) | static_cast<result_type>(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedXorOp {};
|
||||
|
||||
// For simplicity we support only unsigned integers.
|
||||
template <typename T, typename U>
|
||||
struct ClampedXorOp<T,
|
||||
U,
|
||||
typename std::enable_if<std::is_integral<T>::value &&
|
||||
std::is_integral<U>::value>::type> {
|
||||
using result_type = typename std::make_unsigned<
|
||||
typename MaxExponentPromotion<T, U>::type>::type;
|
||||
template <typename V>
|
||||
static constexpr V Do(T x, U y) {
|
||||
return static_cast<result_type>(x) ^ static_cast<result_type>(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedMaxOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedMaxOp<
|
||||
T,
|
||||
U,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||
std::is_arithmetic<U>::value>::type> {
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U y) {
|
||||
return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
|
||||
: saturated_cast<V>(y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, class Enable = void>
|
||||
struct ClampedMinOp {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedMinOp<
|
||||
T,
|
||||
U,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||
std::is_arithmetic<U>::value>::type> {
|
||||
using result_type = typename LowestValuePromotion<T, U>::type;
|
||||
template <typename V = result_type>
|
||||
static constexpr V Do(T x, U y) {
|
||||
return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
|
||||
: saturated_cast<V>(y);
|
||||
}
|
||||
};
|
||||
|
||||
// This is just boilerplate that wraps the standard floating point arithmetic.
|
||||
// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
|
||||
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
|
||||
template <typename T, typename U> \
|
||||
struct Clamped##NAME##Op< \
|
||||
T, U, \
|
||||
typename std::enable_if<std::is_floating_point<T>::value || \
|
||||
std::is_floating_point<U>::value>::type> { \
|
||||
using result_type = typename MaxExponentPromotion<T, U>::type; \
|
||||
template <typename V = result_type> \
|
||||
static constexpr V Do(T x, U y) { \
|
||||
return saturated_cast<V>(x OP y); \
|
||||
} \
|
||||
};
|
||||
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Add, +)
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
|
||||
BASE_FLOAT_ARITHMETIC_OPS(Div, /)
|
||||
|
||||
#undef BASE_FLOAT_ARITHMETIC_OPS
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
|
||||
19
oss/chromium/base/numerics/math_constants.h
Normal file
19
oss/chromium/base/numerics/math_constants.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_MATH_CONSTANTS_H_
|
||||
#define BASE_NUMERICS_MATH_CONSTANTS_H_
|
||||
|
||||
namespace base {
|
||||
|
||||
constexpr double kPiDouble = 3.14159265358979323846;
|
||||
constexpr float kPiFloat = 3.14159265358979323846f;
|
||||
|
||||
// The mean acceleration due to gravity on Earth in m/s^2.
|
||||
constexpr double kMeanGravityDouble = 9.80665;
|
||||
constexpr float kMeanGravityFloat = 9.80665f;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_MATH_CONSTANTS_H_
|
||||
27
oss/chromium/base/numerics/ranges.h
Normal file
27
oss/chromium/base/numerics/ranges.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_RANGES_H_
|
||||
#define BASE_NUMERICS_RANGES_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace base {
|
||||
|
||||
// To be replaced with std::clamp() from C++17, someday.
|
||||
template <class T>
|
||||
constexpr const T& ClampToRange(const T& value, const T& min, const T& max) {
|
||||
return std::min(std::max(value, min), max);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsApproximatelyEqual(T lhs, T rhs, T tolerance) {
|
||||
static_assert(std::is_arithmetic<T>::value, "Argument must be arithmetic");
|
||||
return std::abs(rhs - lhs) <= tolerance;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_RANGES_H_
|
||||
358
oss/chromium/base/numerics/safe_conversions.h
Normal file
358
oss/chromium/base/numerics/safe_conversions.h
Normal file
@@ -0,0 +1,358 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||
#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions_impl.h"
|
||||
|
||||
#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
|
||||
#include "base/numerics/safe_conversions_arm_impl.h"
|
||||
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
|
||||
#else
|
||||
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
|
||||
#endif
|
||||
|
||||
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||
#include <ostream>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
|
||||
template <typename Dst, typename Src>
|
||||
struct SaturateFastAsmOp {
|
||||
static const bool is_supported = false;
|
||||
static constexpr Dst Do(Src) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<Dst>();
|
||||
}
|
||||
};
|
||||
#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
|
||||
#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
|
||||
|
||||
// The following special case a few specific integer conversions where we can
|
||||
// eke out better performance than range checking.
|
||||
template <typename Dst, typename Src, typename Enable = void>
|
||||
struct IsValueInRangeFastOp {
|
||||
static const bool is_supported = false;
|
||||
static constexpr bool Do(Src value) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<bool>();
|
||||
}
|
||||
};
|
||||
|
||||
// Signed to signed range comparison.
|
||||
template <typename Dst, typename Src>
|
||||
struct IsValueInRangeFastOp<
|
||||
Dst,
|
||||
Src,
|
||||
typename std::enable_if<
|
||||
std::is_integral<Dst>::value && std::is_integral<Src>::value &&
|
||||
std::is_signed<Dst>::value && std::is_signed<Src>::value &&
|
||||
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
|
||||
static const bool is_supported = true;
|
||||
|
||||
static constexpr bool Do(Src value) {
|
||||
// Just downcast to the smaller type, sign extend it back to the original
|
||||
// type, and then see if it matches the original value.
|
||||
return value == static_cast<Dst>(value);
|
||||
}
|
||||
};
|
||||
|
||||
// Signed to unsigned range comparison.
|
||||
template <typename Dst, typename Src>
|
||||
struct IsValueInRangeFastOp<
|
||||
Dst,
|
||||
Src,
|
||||
typename std::enable_if<
|
||||
std::is_integral<Dst>::value && std::is_integral<Src>::value &&
|
||||
!std::is_signed<Dst>::value && std::is_signed<Src>::value &&
|
||||
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
|
||||
static const bool is_supported = true;
|
||||
|
||||
static constexpr bool Do(Src value) {
|
||||
// We cast a signed as unsigned to overflow negative values to the top,
|
||||
// then compare against whichever maximum is smaller, as our upper bound.
|
||||
return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
|
||||
}
|
||||
};
|
||||
|
||||
// Convenience function that returns true if the supplied value is in range
|
||||
// for the destination type.
|
||||
template <typename Dst, typename Src>
|
||||
constexpr bool IsValueInRangeForNumericType(Src value) {
|
||||
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||
return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
|
||||
? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
|
||||
static_cast<SrcType>(value))
|
||||
: internal::DstRangeRelationToSrcRange<Dst>(
|
||||
static_cast<SrcType>(value))
|
||||
.IsValid();
|
||||
}
|
||||
|
||||
// checked_cast<> is analogous to static_cast<> for numeric types,
|
||||
// except that it CHECKs that the specified numeric conversion will not
|
||||
// overflow or underflow. NaN source will always trigger a CHECK.
|
||||
template <typename Dst,
|
||||
class CheckHandler = internal::CheckOnFailure,
|
||||
typename Src>
|
||||
constexpr Dst checked_cast(Src value) {
|
||||
// This throws a compile-time error on evaluating the constexpr if it can be
|
||||
// determined at compile-time as failing, otherwise it will CHECK at runtime.
|
||||
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||
return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
|
||||
? static_cast<Dst>(static_cast<SrcType>(value))
|
||||
: CheckHandler::template HandleFailure<Dst>();
|
||||
}
|
||||
|
||||
// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
|
||||
// You may provide your own limits (e.g. to saturated_cast) so long as you
|
||||
// implement all of the static constexpr member functions in the class below.
|
||||
template <typename T>
|
||||
struct SaturationDefaultLimits : public std::numeric_limits<T> {
|
||||
static constexpr T NaN() {
|
||||
return std::numeric_limits<T>::has_quiet_NaN
|
||||
? std::numeric_limits<T>::quiet_NaN()
|
||||
: T();
|
||||
}
|
||||
using std::numeric_limits<T>::max;
|
||||
static constexpr T Overflow() {
|
||||
return std::numeric_limits<T>::has_infinity
|
||||
? std::numeric_limits<T>::infinity()
|
||||
: std::numeric_limits<T>::max();
|
||||
}
|
||||
using std::numeric_limits<T>::lowest;
|
||||
static constexpr T Underflow() {
|
||||
return std::numeric_limits<T>::has_infinity
|
||||
? std::numeric_limits<T>::infinity() * -1
|
||||
: std::numeric_limits<T>::lowest();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, template <typename> class S, typename Src>
|
||||
constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
|
||||
// For some reason clang generates much better code when the branch is
|
||||
// structured exactly this way, rather than a sequence of checks.
|
||||
return !constraint.IsOverflowFlagSet()
|
||||
? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
|
||||
: S<Dst>::Underflow())
|
||||
// Skip this check for integral Src, which cannot be NaN.
|
||||
: (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
|
||||
? S<Dst>::Overflow()
|
||||
: S<Dst>::NaN());
|
||||
}
|
||||
|
||||
// We can reduce the number of conditions and get slightly better performance
|
||||
// for normal signed and unsigned integer ranges. And in the specific case of
|
||||
// Arm, we can use the optimized saturation instructions.
|
||||
template <typename Dst, typename Src, typename Enable = void>
|
||||
struct SaturateFastOp {
|
||||
static const bool is_supported = false;
|
||||
static constexpr Dst Do(Src value) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<Dst>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct SaturateFastOp<
|
||||
Dst,
|
||||
Src,
|
||||
typename std::enable_if<std::is_integral<Src>::value &&
|
||||
std::is_integral<Dst>::value &&
|
||||
SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
|
||||
static const bool is_supported = true;
|
||||
static Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); }
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct SaturateFastOp<
|
||||
Dst,
|
||||
Src,
|
||||
typename std::enable_if<std::is_integral<Src>::value &&
|
||||
std::is_integral<Dst>::value &&
|
||||
!SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
|
||||
static const bool is_supported = true;
|
||||
static Dst Do(Src value) {
|
||||
// The exact order of the following is structured to hit the correct
|
||||
// optimization heuristics across compilers. Do not change without
|
||||
// checking the emitted code.
|
||||
Dst saturated = CommonMaxOrMin<Dst, Src>(
|
||||
IsMaxInRangeForNumericType<Dst, Src>() ||
|
||||
(!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
|
||||
return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
|
||||
? static_cast<Dst>(value)
|
||||
: saturated;
|
||||
}
|
||||
};
|
||||
|
||||
// saturated_cast<> is analogous to static_cast<> for numeric types, except
|
||||
// that the specified numeric conversion will saturate by default rather than
|
||||
// overflow or underflow, and NaN assignment to an integral will return 0.
|
||||
// All boundary condition behaviors can be overriden with a custom handler.
|
||||
template <typename Dst,
|
||||
template <typename> class SaturationHandler = SaturationDefaultLimits,
|
||||
typename Src>
|
||||
constexpr Dst saturated_cast(Src value) {
|
||||
using SrcType = typename UnderlyingType<Src>::type;
|
||||
return !IsCompileTimeConstant(value) &&
|
||||
SaturateFastOp<Dst, SrcType>::is_supported &&
|
||||
std::is_same<SaturationHandler<Dst>,
|
||||
SaturationDefaultLimits<Dst>>::value
|
||||
? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
|
||||
: saturated_cast_impl<Dst, SaturationHandler, SrcType>(
|
||||
static_cast<SrcType>(value),
|
||||
DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
|
||||
static_cast<SrcType>(value)));
|
||||
}
|
||||
|
||||
// strict_cast<> is analogous to static_cast<> for numeric types, except that
|
||||
// it will cause a compile failure if the destination type is not large enough
|
||||
// to contain any value in the source type. It performs no runtime checking.
|
||||
template <typename Dst, typename Src>
|
||||
constexpr Dst strict_cast(Src value) {
|
||||
using SrcType = typename UnderlyingType<Src>::type;
|
||||
static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
|
||||
static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
|
||||
|
||||
// If you got here from a compiler error, it's because you tried to assign
|
||||
// from a source type to a destination type that has insufficient range.
|
||||
// The solution may be to change the destination type you're assigning to,
|
||||
// and use one large enough to represent the source.
|
||||
// Alternatively, you may be better served with the checked_cast<> or
|
||||
// saturated_cast<> template functions for your particular use case.
|
||||
static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
|
||||
NUMERIC_RANGE_CONTAINED,
|
||||
"The source type is out of range for the destination type. "
|
||||
"Please see strict_cast<> comments for more information.");
|
||||
|
||||
return static_cast<Dst>(static_cast<SrcType>(value));
|
||||
}
|
||||
|
||||
// Some wrappers to statically check that a type is in range.
|
||||
template <typename Dst, typename Src, class Enable = void>
|
||||
struct IsNumericRangeContained {
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct IsNumericRangeContained<
|
||||
Dst,
|
||||
Src,
|
||||
typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
|
||||
ArithmeticOrUnderlyingEnum<Src>::value>::type> {
|
||||
static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
|
||||
NUMERIC_RANGE_CONTAINED;
|
||||
};
|
||||
|
||||
// StrictNumeric implements compile time range checking between numeric types by
|
||||
// wrapping assignment operations in a strict_cast. This class is intended to be
|
||||
// used for function arguments and return types, to ensure the destination type
|
||||
// can always contain the source type. This is essentially the same as enforcing
|
||||
// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
|
||||
// incrementally at API boundaries, making it easier to convert code so that it
|
||||
// compiles cleanly with truncation warnings enabled.
|
||||
// This template should introduce no runtime overhead, but it also provides no
|
||||
// runtime checking of any of the associated mathematical operations. Use
|
||||
// CheckedNumeric for runtime range checks of the actual value being assigned.
|
||||
template <typename T>
|
||||
class StrictNumeric {
|
||||
public:
|
||||
using type = T;
|
||||
|
||||
constexpr StrictNumeric() : value_(0) {}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
|
||||
: value_(strict_cast<T>(rhs.value_)) {}
|
||||
|
||||
// This is not an explicit constructor because we implicitly upgrade regular
|
||||
// numerics to StrictNumerics to make them easier to use.
|
||||
template <typename Src>
|
||||
constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
|
||||
: value_(strict_cast<T>(value)) {}
|
||||
|
||||
// If you got here from a compiler error, it's because you tried to assign
|
||||
// from a source type to a destination type that has insufficient range.
|
||||
// The solution may be to change the destination type you're assigning to,
|
||||
// and use one large enough to represent the source.
|
||||
// If you're assigning from a CheckedNumeric<> class, you may be able to use
|
||||
// the AssignIfValid() member function, specify a narrower destination type to
|
||||
// the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
|
||||
// of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
|
||||
// If you've encountered an _ambiguous overload_ you can use a static_cast<>
|
||||
// to explicitly cast the result to the destination type.
|
||||
// If none of that works, you may be better served with the checked_cast<> or
|
||||
// saturated_cast<> template functions for your particular use case.
|
||||
template <typename Dst,
|
||||
typename std::enable_if<
|
||||
IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
|
||||
constexpr operator Dst() const {
|
||||
return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
|
||||
}
|
||||
|
||||
private:
|
||||
const T value_;
|
||||
};
|
||||
|
||||
// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
|
||||
template <typename T>
|
||||
constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
|
||||
const T value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||
// Overload the ostream output operator to make logging work nicely.
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
|
||||
os << static_cast<T>(value);
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
|
||||
template <typename L, typename R, \
|
||||
typename std::enable_if< \
|
||||
internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
|
||||
constexpr bool operator OP(const L lhs, const R rhs) { \
|
||||
return SafeCompare<NAME, typename UnderlyingType<L>::type, \
|
||||
typename UnderlyingType<R>::type>(lhs, rhs); \
|
||||
}
|
||||
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
|
||||
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)
|
||||
|
||||
} // namespace internal
|
||||
|
||||
using internal::as_signed;
|
||||
using internal::as_unsigned;
|
||||
using internal::checked_cast;
|
||||
using internal::strict_cast;
|
||||
using internal::saturated_cast;
|
||||
using internal::SafeUnsignedAbs;
|
||||
using internal::StrictNumeric;
|
||||
using internal::MakeStrictNum;
|
||||
using internal::IsValueInRangeForNumericType;
|
||||
using internal::IsTypeInRangeForNumericType;
|
||||
using internal::IsValueNegative;
|
||||
|
||||
// Explicitly make a shorter size_t alias for convenience.
|
||||
using SizeT = StrictNumeric<size_t>;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||
51
oss/chromium/base/numerics/safe_conversions_arm_impl.h
Normal file
51
oss/chromium/base/numerics/safe_conversions_arm_impl.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
|
||||
#define BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions_impl.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
// Fast saturation to a destination type.
|
||||
template <typename Dst, typename Src>
|
||||
struct SaturateFastAsmOp {
|
||||
static constexpr bool is_supported =
|
||||
std::is_signed<Src>::value && std::is_integral<Dst>::value &&
|
||||
std::is_integral<Src>::value &&
|
||||
IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&
|
||||
IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&
|
||||
!IsTypeInRangeForNumericType<Dst, Src>::value;
|
||||
|
||||
__attribute__((always_inline)) static Dst Do(Src value) {
|
||||
int32_t src = value;
|
||||
typename std::conditional<std::is_signed<Dst>::value, int32_t,
|
||||
uint32_t>::type result;
|
||||
if (std::is_signed<Dst>::value) {
|
||||
asm("ssat %[dst], %[shift], %[src]"
|
||||
: [dst] "=r"(result)
|
||||
: [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32
|
||||
? IntegerBitsPlusSign<Dst>::value
|
||||
: 32));
|
||||
} else {
|
||||
asm("usat %[dst], %[shift], %[src]"
|
||||
: [dst] "=r"(result)
|
||||
: [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32
|
||||
? IntegerBitsPlusSign<Dst>::value
|
||||
: 31));
|
||||
}
|
||||
return static_cast<Dst>(result);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
|
||||
850
oss/chromium/base/numerics/safe_conversions_impl.h
Normal file
850
oss/chromium/base/numerics/safe_conversions_impl.h
Normal file
@@ -0,0 +1,850 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||
#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define BASE_NUMERICS_LIKELY(x) (x)
|
||||
#define BASE_NUMERICS_UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
// The std library doesn't provide a binary max_exponent for integers, however
|
||||
// we can compute an analog using std::numeric_limits<>::digits.
|
||||
template <typename NumericType>
|
||||
struct MaxExponent {
|
||||
static const int value = std::is_floating_point<NumericType>::value
|
||||
? std::numeric_limits<NumericType>::max_exponent
|
||||
: std::numeric_limits<NumericType>::digits + 1;
|
||||
};
|
||||
|
||||
// The number of bits (including the sign) in an integer. Eliminates sizeof
|
||||
// hacks.
|
||||
template <typename NumericType>
|
||||
struct IntegerBitsPlusSign {
|
||||
static const int value = std::numeric_limits<NumericType>::digits +
|
||||
std::is_signed<NumericType>::value;
|
||||
};
|
||||
|
||||
// Helper templates for integer manipulations.
|
||||
|
||||
template <typename Integer>
|
||||
struct PositionOfSignBit {
|
||||
static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
|
||||
};
|
||||
|
||||
// Determines if a numeric value is negative without throwing compiler
|
||||
// warnings on: unsigned(value) < 0.
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
|
||||
constexpr bool IsValueNegative(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
|
||||
return value < 0;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
|
||||
constexpr bool IsValueNegative(T) {
|
||||
static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This performs a fast negation, returning a signed value. It works on unsigned
|
||||
// arguments, but probably doesn't do what you want for any unsigned value
|
||||
// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
|
||||
template <typename T>
|
||||
constexpr typename std::make_signed<T>::type ConditionalNegate(
|
||||
T x,
|
||||
bool is_negative) {
|
||||
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||
using SignedT = typename std::make_signed<T>::type;
|
||||
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||
return static_cast<SignedT>(
|
||||
(static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
|
||||
}
|
||||
|
||||
// This performs a safe, absolute value via unsigned overflow.
|
||||
template <typename T>
|
||||
constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
|
||||
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||
return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
|
||||
: static_cast<UnsignedT>(value);
|
||||
}
|
||||
|
||||
// This allows us to switch paths on known compile-time constants.
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
constexpr bool CanDetectCompileTimeConstant() {
|
||||
return true;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr bool IsCompileTimeConstant(const T v) {
|
||||
return __builtin_constant_p(v);
|
||||
}
|
||||
#else
|
||||
constexpr bool CanDetectCompileTimeConstant() {
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr bool IsCompileTimeConstant(const T) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
template <typename T>
|
||||
constexpr bool MustTreatAsConstexpr(const T v) {
|
||||
// Either we can't detect a compile-time constant, and must always use the
|
||||
// constexpr path, or we know we have a compile-time constant.
|
||||
return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
|
||||
}
|
||||
|
||||
// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
|
||||
// Also used in a constexpr template to trigger a compilation failure on
|
||||
// an error condition.
|
||||
struct CheckOnFailure {
|
||||
template <typename T>
|
||||
static T HandleFailure() {
|
||||
#if defined(_MSC_VER)
|
||||
__debugbreak();
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
__builtin_trap();
|
||||
#else
|
||||
((void)(*(volatile char*)0 = 0));
|
||||
#endif
|
||||
return T();
|
||||
}
|
||||
};
|
||||
|
||||
enum IntegerRepresentation {
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED
|
||||
};
|
||||
|
||||
// A range for a given nunmeric Src type is contained for a given numeric Dst
|
||||
// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
|
||||
// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
|
||||
// We implement this as template specializations rather than simple static
|
||||
// comparisons to ensure type correctness in our comparisons.
|
||||
enum NumericRangeRepresentation {
|
||||
NUMERIC_RANGE_NOT_CONTAINED,
|
||||
NUMERIC_RANGE_CONTAINED
|
||||
};
|
||||
|
||||
// Helper templates to statically determine if our destination type can contain
|
||||
// maximum and minimum values represented by the source type.
|
||||
|
||||
template <typename Dst,
|
||||
typename Src,
|
||||
IntegerRepresentation DstSign = std::is_signed<Dst>::value
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||
IntegerRepresentation SrcSign = std::is_signed<Src>::value
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED>
|
||||
struct StaticDstRangeRelationToSrcRange;
|
||||
|
||||
// Same sign: Dst is guaranteed to contain Src only if its range is equal or
|
||||
// larger.
|
||||
template <typename Dst, typename Src, IntegerRepresentation Sign>
|
||||
struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
|
||||
static const NumericRangeRepresentation value =
|
||||
MaxExponent<Dst>::value >= MaxExponent<Src>::value
|
||||
? NUMERIC_RANGE_CONTAINED
|
||||
: NUMERIC_RANGE_NOT_CONTAINED;
|
||||
};
|
||||
|
||||
// Unsigned to signed: Dst is guaranteed to contain source only if its range is
|
||||
// larger.
|
||||
template <typename Dst, typename Src>
|
||||
struct StaticDstRangeRelationToSrcRange<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
INTEGER_REPRESENTATION_UNSIGNED> {
|
||||
static const NumericRangeRepresentation value =
|
||||
MaxExponent<Dst>::value > MaxExponent<Src>::value
|
||||
? NUMERIC_RANGE_CONTAINED
|
||||
: NUMERIC_RANGE_NOT_CONTAINED;
|
||||
};
|
||||
|
||||
// Signed to unsigned: Dst cannot be statically determined to contain Src.
|
||||
template <typename Dst, typename Src>
|
||||
struct StaticDstRangeRelationToSrcRange<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED> {
|
||||
static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
|
||||
};
|
||||
|
||||
// This class wraps the range constraints as separate booleans so the compiler
|
||||
// can identify constants and eliminate unused code paths.
|
||||
class RangeCheck {
|
||||
public:
|
||||
constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
|
||||
: is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
|
||||
constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
|
||||
constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
|
||||
constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
|
||||
constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
|
||||
constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
|
||||
constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
|
||||
constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
|
||||
constexpr bool operator==(const RangeCheck rhs) const {
|
||||
return is_underflow_ == rhs.is_underflow_ &&
|
||||
is_overflow_ == rhs.is_overflow_;
|
||||
}
|
||||
constexpr bool operator!=(const RangeCheck rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
// Do not change the order of these member variables. The integral conversion
|
||||
// optimization depends on this exact order.
|
||||
const bool is_underflow_;
|
||||
const bool is_overflow_;
|
||||
};
|
||||
|
||||
// The following helper template addresses a corner case in range checks for
|
||||
// conversion from a floating-point type to an integral type of smaller range
|
||||
// but larger precision (e.g. float -> unsigned). The problem is as follows:
|
||||
// 1. Integral maximum is always one less than a power of two, so it must be
|
||||
// truncated to fit the mantissa of the floating point. The direction of
|
||||
// rounding is implementation defined, but by default it's always IEEE
|
||||
// floats, which round to nearest and thus result in a value of larger
|
||||
// magnitude than the integral value.
|
||||
// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
|
||||
// // is 4294967295u.
|
||||
// 2. If the floating point value is equal to the promoted integral maximum
|
||||
// value, a range check will erroneously pass.
|
||||
// Example: (4294967296f <= 4294967295u) // This is true due to a precision
|
||||
// // loss in rounding up to float.
|
||||
// 3. When the floating point value is then converted to an integral, the
|
||||
// resulting value is out of range for the target integral type and
|
||||
// thus is implementation defined.
|
||||
// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
|
||||
// To fix this bug we manually truncate the maximum value when the destination
|
||||
// type is an integral of larger precision than the source floating-point type,
|
||||
// such that the resulting maximum is represented exactly as a floating point.
|
||||
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||
struct NarrowingRange {
|
||||
using SrcLimits = std::numeric_limits<Src>;
|
||||
using DstLimits = typename std::numeric_limits<Dst>;
|
||||
|
||||
// Computes the mask required to make an accurate comparison between types.
|
||||
static const int kShift =
|
||||
(MaxExponent<Src>::value > MaxExponent<Dst>::value &&
|
||||
SrcLimits::digits < DstLimits::digits)
|
||||
? (DstLimits::digits - SrcLimits::digits)
|
||||
: 0;
|
||||
template <
|
||||
typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
|
||||
// Masks out the integer bits that are beyond the precision of the
|
||||
// intermediate type used for comparison.
|
||||
static constexpr T Adjust(T value) {
|
||||
static_assert(std::is_same<T, Dst>::value, "");
|
||||
static_assert(kShift < DstLimits::digits, "");
|
||||
return static_cast<T>(
|
||||
ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
|
||||
IsValueNegative(value)));
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type* =
|
||||
nullptr>
|
||||
static constexpr T Adjust(T value) {
|
||||
static_assert(std::is_same<T, Dst>::value, "");
|
||||
static_assert(kShift == 0, "");
|
||||
return value;
|
||||
}
|
||||
|
||||
static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
|
||||
static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
|
||||
};
|
||||
|
||||
template <typename Dst,
|
||||
typename Src,
|
||||
template <typename> class Bounds,
|
||||
IntegerRepresentation DstSign = std::is_signed<Dst>::value
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||
IntegerRepresentation SrcSign = std::is_signed<Src>::value
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||
NumericRangeRepresentation DstRange =
|
||||
StaticDstRangeRelationToSrcRange<Dst, Src>::value>
|
||||
struct DstRangeRelationToSrcRangeImpl;
|
||||
|
||||
// The following templates are for ranges that must be verified at runtime. We
|
||||
// split it into checks based on signedness to avoid confusing casts and
|
||||
// compiler warnings on signed an unsigned comparisons.
|
||||
|
||||
// Same sign narrowing: The range is contained for normal limits.
|
||||
template <typename Dst,
|
||||
typename Src,
|
||||
template <typename> class Bounds,
|
||||
IntegerRepresentation DstSign,
|
||||
IntegerRepresentation SrcSign>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
Bounds,
|
||||
DstSign,
|
||||
SrcSign,
|
||||
NUMERIC_RANGE_CONTAINED> {
|
||||
static constexpr RangeCheck Check(Src value) {
|
||||
using SrcLimits = std::numeric_limits<Src>;
|
||||
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||
return RangeCheck(
|
||||
static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
|
||||
static_cast<Dst>(value) >= DstLimits::lowest(),
|
||||
static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
|
||||
static_cast<Dst>(value) <= DstLimits::max());
|
||||
}
|
||||
};
|
||||
|
||||
// Signed to signed narrowing: Both the upper and lower boundaries may be
|
||||
// exceeded for standard limits.
|
||||
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
Bounds,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||
static constexpr RangeCheck Check(Src value) {
|
||||
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||
return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
|
||||
// standard limits.
|
||||
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
Bounds,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||
static constexpr RangeCheck Check(Src value) {
|
||||
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||
return RangeCheck(
|
||||
DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
|
||||
value <= DstLimits::max());
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
|
||||
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
Bounds,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||
static constexpr RangeCheck Check(Src value) {
|
||||
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||
using Promotion = decltype(Src() + Dst());
|
||||
return RangeCheck(DstLimits::lowest() <= Dst(0) ||
|
||||
static_cast<Promotion>(value) >=
|
||||
static_cast<Promotion>(DstLimits::lowest()),
|
||||
static_cast<Promotion>(value) <=
|
||||
static_cast<Promotion>(DstLimits::max()));
|
||||
}
|
||||
};
|
||||
|
||||
// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
|
||||
// and any negative value exceeds the lower boundary for standard limits.
|
||||
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
Bounds,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||
static constexpr RangeCheck Check(Src value) {
|
||||
using SrcLimits = std::numeric_limits<Src>;
|
||||
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||
using Promotion = decltype(Src() + Dst());
|
||||
return RangeCheck(
|
||||
value >= Src(0) && (DstLimits::lowest() == 0 ||
|
||||
static_cast<Dst>(value) >= DstLimits::lowest()),
|
||||
static_cast<Promotion>(SrcLimits::max()) <=
|
||||
static_cast<Promotion>(DstLimits::max()) ||
|
||||
static_cast<Promotion>(value) <=
|
||||
static_cast<Promotion>(DstLimits::max()));
|
||||
}
|
||||
};
|
||||
|
||||
// Simple wrapper for statically checking if a type's range is contained.
|
||||
template <typename Dst, typename Src>
|
||||
struct IsTypeInRangeForNumericType {
|
||||
static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
|
||||
NUMERIC_RANGE_CONTAINED;
|
||||
};
|
||||
|
||||
template <typename Dst,
|
||||
template <typename> class Bounds = std::numeric_limits,
|
||||
typename Src>
|
||||
constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
|
||||
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||
static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
|
||||
static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
|
||||
return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
|
||||
}
|
||||
|
||||
// Integer promotion templates used by the portable checked integer arithmetic.
|
||||
template <size_t Size, bool IsSigned>
|
||||
struct IntegerForDigitsAndSign;
|
||||
|
||||
#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
|
||||
template <> \
|
||||
struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
|
||||
std::is_signed<I>::value> { \
|
||||
using type = I; \
|
||||
}
|
||||
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
|
||||
INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
|
||||
#undef INTEGER_FOR_DIGITS_AND_SIGN
|
||||
|
||||
// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
|
||||
// support 128-bit math, then the ArithmeticPromotion template below will need
|
||||
// to be updated (or more likely replaced with a decltype expression).
|
||||
static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
|
||||
"Max integer size not supported for this toolchain.");
|
||||
|
||||
template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
|
||||
struct TwiceWiderInteger {
|
||||
using type =
|
||||
typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
|
||||
IsSigned>::type;
|
||||
};
|
||||
|
||||
enum ArithmeticPromotionCategory {
|
||||
LEFT_PROMOTION, // Use the type of the left-hand argument.
|
||||
RIGHT_PROMOTION // Use the type of the right-hand argument.
|
||||
};
|
||||
|
||||
// Determines the type that can represent the largest positive value.
|
||||
template <typename Lhs,
|
||||
typename Rhs,
|
||||
ArithmeticPromotionCategory Promotion =
|
||||
(MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
|
||||
? LEFT_PROMOTION
|
||||
: RIGHT_PROMOTION>
|
||||
struct MaxExponentPromotion;
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
|
||||
using type = Lhs;
|
||||
};
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
|
||||
using type = Rhs;
|
||||
};
|
||||
|
||||
// Determines the type that can represent the lowest arithmetic value.
|
||||
template <typename Lhs,
|
||||
typename Rhs,
|
||||
ArithmeticPromotionCategory Promotion =
|
||||
std::is_signed<Lhs>::value
|
||||
? (std::is_signed<Rhs>::value
|
||||
? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
|
||||
? LEFT_PROMOTION
|
||||
: RIGHT_PROMOTION)
|
||||
: LEFT_PROMOTION)
|
||||
: (std::is_signed<Rhs>::value
|
||||
? RIGHT_PROMOTION
|
||||
: (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
|
||||
? LEFT_PROMOTION
|
||||
: RIGHT_PROMOTION))>
|
||||
struct LowestValuePromotion;
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
|
||||
using type = Lhs;
|
||||
};
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
|
||||
using type = Rhs;
|
||||
};
|
||||
|
||||
// Determines the type that is best able to represent an arithmetic result.
|
||||
template <
|
||||
typename Lhs,
|
||||
typename Rhs = Lhs,
|
||||
bool is_intmax_type =
|
||||
std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
|
||||
IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
|
||||
value == IntegerBitsPlusSign<intmax_t>::value,
|
||||
bool is_max_exponent =
|
||||
StaticDstRangeRelationToSrcRange<
|
||||
typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||
Lhs>::value ==
|
||||
NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
|
||||
typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||
Rhs>::value == NUMERIC_RANGE_CONTAINED>
|
||||
struct BigEnoughPromotion;
|
||||
|
||||
// The side with the max exponent is big enough.
|
||||
template <typename Lhs, typename Rhs, bool is_intmax_type>
|
||||
struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
|
||||
using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
|
||||
static const bool is_contained = true;
|
||||
};
|
||||
|
||||
// We can use a twice wider type to fit.
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct BigEnoughPromotion<Lhs, Rhs, false, false> {
|
||||
using type =
|
||||
typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||
std::is_signed<Lhs>::value ||
|
||||
std::is_signed<Rhs>::value>::type;
|
||||
static const bool is_contained = true;
|
||||
};
|
||||
|
||||
// No type is large enough.
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct BigEnoughPromotion<Lhs, Rhs, true, false> {
|
||||
using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
|
||||
static const bool is_contained = false;
|
||||
};
|
||||
|
||||
// We can statically check if operations on the provided types can wrap, so we
|
||||
// can skip the checked operations if they're not needed. So, for an integer we
|
||||
// care if the destination type preserves the sign and is twice the width of
|
||||
// the source.
|
||||
template <typename T, typename Lhs, typename Rhs = Lhs>
|
||||
struct IsIntegerArithmeticSafe {
|
||||
static const bool value =
|
||||
!std::is_floating_point<T>::value &&
|
||||
!std::is_floating_point<Lhs>::value &&
|
||||
!std::is_floating_point<Rhs>::value &&
|
||||
std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
|
||||
IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
|
||||
std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
|
||||
IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
|
||||
};
|
||||
|
||||
// Promotes to a type that can represent any possible result of a binary
|
||||
// arithmetic operation with the source types.
|
||||
template <typename Lhs,
|
||||
typename Rhs,
|
||||
bool is_promotion_possible = IsIntegerArithmeticSafe<
|
||||
typename std::conditional<std::is_signed<Lhs>::value ||
|
||||
std::is_signed<Rhs>::value,
|
||||
intmax_t,
|
||||
uintmax_t>::type,
|
||||
typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
|
||||
struct FastIntegerArithmeticPromotion;
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
|
||||
using type =
|
||||
typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||
std::is_signed<Lhs>::value ||
|
||||
std::is_signed<Rhs>::value>::type;
|
||||
static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
|
||||
static const bool is_contained = true;
|
||||
};
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
|
||||
using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
|
||||
static const bool is_contained = false;
|
||||
};
|
||||
|
||||
// Extracts the underlying type from an enum.
|
||||
template <typename T, bool is_enum = std::is_enum<T>::value>
|
||||
struct ArithmeticOrUnderlyingEnum;
|
||||
|
||||
template <typename T>
|
||||
struct ArithmeticOrUnderlyingEnum<T, true> {
|
||||
using type = typename std::underlying_type<T>::type;
|
||||
static const bool value = std::is_arithmetic<type>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ArithmeticOrUnderlyingEnum<T, false> {
|
||||
using type = T;
|
||||
static const bool value = std::is_arithmetic<type>::value;
|
||||
};
|
||||
|
||||
// The following are helper templates used in the CheckedNumeric class.
|
||||
template <typename T>
|
||||
class CheckedNumeric;
|
||||
|
||||
template <typename T>
|
||||
class ClampedNumeric;
|
||||
|
||||
template <typename T>
|
||||
class StrictNumeric;
|
||||
|
||||
// Used to treat CheckedNumeric and arithmetic underlying types the same.
|
||||
template <typename T>
|
||||
struct UnderlyingType {
|
||||
using type = typename ArithmeticOrUnderlyingEnum<T>::type;
|
||||
static const bool is_numeric = std::is_arithmetic<type>::value;
|
||||
static const bool is_checked = false;
|
||||
static const bool is_clamped = false;
|
||||
static const bool is_strict = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct UnderlyingType<CheckedNumeric<T>> {
|
||||
using type = T;
|
||||
static const bool is_numeric = true;
|
||||
static const bool is_checked = true;
|
||||
static const bool is_clamped = false;
|
||||
static const bool is_strict = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct UnderlyingType<ClampedNumeric<T>> {
|
||||
using type = T;
|
||||
static const bool is_numeric = true;
|
||||
static const bool is_checked = false;
|
||||
static const bool is_clamped = true;
|
||||
static const bool is_strict = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct UnderlyingType<StrictNumeric<T>> {
|
||||
using type = T;
|
||||
static const bool is_numeric = true;
|
||||
static const bool is_checked = false;
|
||||
static const bool is_clamped = false;
|
||||
static const bool is_strict = true;
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsCheckedOp {
|
||||
static const bool value =
|
||||
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
|
||||
(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsClampedOp {
|
||||
static const bool value =
|
||||
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
|
||||
(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
|
||||
!(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsStrictOp {
|
||||
static const bool value =
|
||||
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
|
||||
(UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
|
||||
!(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
|
||||
!(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
|
||||
};
|
||||
|
||||
// as_signed<> returns the supplied integral value (or integral castable
|
||||
// Numeric template) cast as a signed integral of equivalent precision.
|
||||
// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
|
||||
template <typename Src>
|
||||
constexpr typename std::make_signed<
|
||||
typename base::internal::UnderlyingType<Src>::type>::type
|
||||
as_signed(const Src value) {
|
||||
static_assert(std::is_integral<decltype(as_signed(value))>::value,
|
||||
"Argument must be a signed or unsigned integer type.");
|
||||
return static_cast<decltype(as_signed(value))>(value);
|
||||
}
|
||||
|
||||
// as_unsigned<> returns the supplied integral value (or integral castable
|
||||
// Numeric template) cast as an unsigned integral of equivalent precision.
|
||||
// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
|
||||
template <typename Src>
|
||||
constexpr typename std::make_unsigned<
|
||||
typename base::internal::UnderlyingType<Src>::type>::type
|
||||
as_unsigned(const Src value) {
|
||||
static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
|
||||
"Argument must be a signed or unsigned integer type.");
|
||||
return static_cast<decltype(as_unsigned(value))>(value);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr bool IsLessImpl(const L lhs,
|
||||
const R rhs,
|
||||
const RangeCheck l_range,
|
||||
const RangeCheck r_range) {
|
||||
return l_range.IsUnderflow() || r_range.IsOverflow() ||
|
||||
(l_range == r_range &&
|
||||
static_cast<decltype(lhs + rhs)>(lhs) <
|
||||
static_cast<decltype(lhs + rhs)>(rhs));
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsLess {
|
||||
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||
"Types must be numeric.");
|
||||
static constexpr bool Test(const L lhs, const R rhs) {
|
||||
return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||
DstRangeRelationToSrcRange<L>(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr bool IsLessOrEqualImpl(const L lhs,
|
||||
const R rhs,
|
||||
const RangeCheck l_range,
|
||||
const RangeCheck r_range) {
|
||||
return l_range.IsUnderflow() || r_range.IsOverflow() ||
|
||||
(l_range == r_range &&
|
||||
static_cast<decltype(lhs + rhs)>(lhs) <=
|
||||
static_cast<decltype(lhs + rhs)>(rhs));
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsLessOrEqual {
|
||||
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||
"Types must be numeric.");
|
||||
static constexpr bool Test(const L lhs, const R rhs) {
|
||||
return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||
DstRangeRelationToSrcRange<L>(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr bool IsGreaterImpl(const L lhs,
|
||||
const R rhs,
|
||||
const RangeCheck l_range,
|
||||
const RangeCheck r_range) {
|
||||
return l_range.IsOverflow() || r_range.IsUnderflow() ||
|
||||
(l_range == r_range &&
|
||||
static_cast<decltype(lhs + rhs)>(lhs) >
|
||||
static_cast<decltype(lhs + rhs)>(rhs));
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsGreater {
|
||||
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||
"Types must be numeric.");
|
||||
static constexpr bool Test(const L lhs, const R rhs) {
|
||||
return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||
DstRangeRelationToSrcRange<L>(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr bool IsGreaterOrEqualImpl(const L lhs,
|
||||
const R rhs,
|
||||
const RangeCheck l_range,
|
||||
const RangeCheck r_range) {
|
||||
return l_range.IsOverflow() || r_range.IsUnderflow() ||
|
||||
(l_range == r_range &&
|
||||
static_cast<decltype(lhs + rhs)>(lhs) >=
|
||||
static_cast<decltype(lhs + rhs)>(rhs));
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsGreaterOrEqual {
|
||||
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||
"Types must be numeric.");
|
||||
static constexpr bool Test(const L lhs, const R rhs) {
|
||||
return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||
DstRangeRelationToSrcRange<L>(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsEqual {
|
||||
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||
"Types must be numeric.");
|
||||
static constexpr bool Test(const L lhs, const R rhs) {
|
||||
return DstRangeRelationToSrcRange<R>(lhs) ==
|
||||
DstRangeRelationToSrcRange<L>(rhs) &&
|
||||
static_cast<decltype(lhs + rhs)>(lhs) ==
|
||||
static_cast<decltype(lhs + rhs)>(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct IsNotEqual {
|
||||
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||
"Types must be numeric.");
|
||||
static constexpr bool Test(const L lhs, const R rhs) {
|
||||
return DstRangeRelationToSrcRange<R>(lhs) !=
|
||||
DstRangeRelationToSrcRange<L>(rhs) ||
|
||||
static_cast<decltype(lhs + rhs)>(lhs) !=
|
||||
static_cast<decltype(lhs + rhs)>(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// These perform the actual math operations on the CheckedNumerics.
|
||||
// Binary arithmetic operations.
|
||||
template <template <typename, typename> class C, typename L, typename R>
|
||||
constexpr bool SafeCompare(const L lhs, const R rhs) {
|
||||
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||
"Types must be numeric.");
|
||||
using Promotion = BigEnoughPromotion<L, R>;
|
||||
using BigType = typename Promotion::type;
|
||||
return Promotion::is_contained
|
||||
// Force to a larger type for speed if both are contained.
|
||||
? C<BigType, BigType>::Test(
|
||||
static_cast<BigType>(static_cast<L>(lhs)),
|
||||
static_cast<BigType>(static_cast<R>(rhs)))
|
||||
// Let the template functions figure it out for mixed types.
|
||||
: C<L, R>::Test(lhs, rhs);
|
||||
}
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr bool IsMaxInRangeForNumericType() {
|
||||
return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
|
||||
std::numeric_limits<Src>::max());
|
||||
}
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr bool IsMinInRangeForNumericType() {
|
||||
return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
|
||||
std::numeric_limits<Src>::lowest());
|
||||
}
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr Dst CommonMax() {
|
||||
return !IsMaxInRangeForNumericType<Dst, Src>()
|
||||
? Dst(std::numeric_limits<Dst>::max())
|
||||
: Dst(std::numeric_limits<Src>::max());
|
||||
}
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr Dst CommonMin() {
|
||||
return !IsMinInRangeForNumericType<Dst, Src>()
|
||||
? Dst(std::numeric_limits<Dst>::lowest())
|
||||
: Dst(std::numeric_limits<Src>::lowest());
|
||||
}
|
||||
|
||||
// This is a wrapper to generate return the max or min for a supplied type.
|
||||
// If the argument is false, the returned value is the maximum. If true the
|
||||
// returned value is the minimum.
|
||||
template <typename Dst, typename Src = Dst>
|
||||
constexpr Dst CommonMaxOrMin(bool is_min) {
|
||||
return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||
12
oss/chromium/base/numerics/safe_math.h
Normal file
12
oss/chromium/base/numerics/safe_math.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_MATH_H_
|
||||
#define BASE_NUMERICS_SAFE_MATH_H_
|
||||
|
||||
#include "base/numerics/checked_math.h"
|
||||
#include "base/numerics/clamped_math.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_MATH_H_
|
||||
122
oss/chromium/base/numerics/safe_math_arm_impl.h
Normal file
122
oss/chromium/base/numerics/safe_math_arm_impl.h
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
|
||||
#define BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedMulFastAsmOp {
|
||||
static const bool is_supported =
|
||||
FastIntegerArithmeticPromotion<T, U>::is_contained;
|
||||
|
||||
// The following is much more efficient than the Clang and GCC builtins for
|
||||
// performing overflow-checked multiplication when a twice wider type is
|
||||
// available. The below compiles down to 2-3 instructions, depending on the
|
||||
// width of the types in use.
|
||||
// As an example, an int32_t multiply compiles to:
|
||||
// smull r0, r1, r0, r1
|
||||
// cmp r1, r1, asr #31
|
||||
// And an int16_t multiply compiles to:
|
||||
// smulbb r1, r1, r0
|
||||
// asr r2, r1, #16
|
||||
// cmp r2, r1, asr #15
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static bool Do(T x, U y, V* result) {
|
||||
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||
Promotion presult;
|
||||
|
||||
presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
|
||||
*result = static_cast<V>(presult);
|
||||
return IsValueInRangeForNumericType<V>(presult);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedAddFastAsmOp {
|
||||
static const bool is_supported =
|
||||
BigEnoughPromotion<T, U>::is_contained &&
|
||||
IsTypeInRangeForNumericType<
|
||||
int32_t,
|
||||
typename BigEnoughPromotion<T, U>::type>::value;
|
||||
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||
// This will get promoted to an int, so let the compiler do whatever is
|
||||
// clever and rely on the saturated cast to bounds check.
|
||||
if (IsIntegerArithmeticSafe<int, T, U>::value)
|
||||
return saturated_cast<V>(x + y);
|
||||
|
||||
int32_t result;
|
||||
int32_t x_i32 = checked_cast<int32_t>(x);
|
||||
int32_t y_i32 = checked_cast<int32_t>(y);
|
||||
|
||||
asm("qadd %[result], %[first], %[second]"
|
||||
: [result] "=r"(result)
|
||||
: [first] "r"(x_i32), [second] "r"(y_i32));
|
||||
return saturated_cast<V>(result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedSubFastAsmOp {
|
||||
static const bool is_supported =
|
||||
BigEnoughPromotion<T, U>::is_contained &&
|
||||
IsTypeInRangeForNumericType<
|
||||
int32_t,
|
||||
typename BigEnoughPromotion<T, U>::type>::value;
|
||||
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||
// This will get promoted to an int, so let the compiler do whatever is
|
||||
// clever and rely on the saturated cast to bounds check.
|
||||
if (IsIntegerArithmeticSafe<int, T, U>::value)
|
||||
return saturated_cast<V>(x - y);
|
||||
|
||||
int32_t result;
|
||||
int32_t x_i32 = checked_cast<int32_t>(x);
|
||||
int32_t y_i32 = checked_cast<int32_t>(y);
|
||||
|
||||
asm("qsub %[result], %[first], %[second]"
|
||||
: [result] "=r"(result)
|
||||
: [first] "r"(x_i32), [second] "r"(y_i32));
|
||||
return saturated_cast<V>(result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedMulFastAsmOp {
|
||||
static const bool is_supported = CheckedMulFastAsmOp<T, U>::is_supported;
|
||||
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||
// Use the CheckedMulFastAsmOp for full-width 32-bit values, because
|
||||
// it's fewer instructions than promoting and then saturating.
|
||||
if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&
|
||||
!IsIntegerArithmeticSafe<uint32_t, T, U>::value) {
|
||||
V result;
|
||||
if (CheckedMulFastAsmOp<T, U>::Do(x, y, &result))
|
||||
return result;
|
||||
return CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
|
||||
}
|
||||
|
||||
assert((FastIntegerArithmeticPromotion<T, U>::is_contained));
|
||||
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||
return saturated_cast<V>(static_cast<Promotion>(x) *
|
||||
static_cast<Promotion>(y));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
|
||||
157
oss/chromium/base/numerics/safe_math_clang_gcc_impl.h
Normal file
157
oss/chromium/base/numerics/safe_math_clang_gcc_impl.h
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
|
||||
#define BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
|
||||
#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
|
||||
#include "base/numerics/safe_math_arm_impl.h"
|
||||
#define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
|
||||
#else
|
||||
#define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
// These are the non-functioning boilerplate implementations of the optimized
|
||||
// safe math routines.
|
||||
#if !BASE_HAS_ASSEMBLER_SAFE_MATH
|
||||
template <typename T, typename U>
|
||||
struct CheckedMulFastAsmOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T, U, V*) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<bool>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedAddFastAsmOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr V Do(T, U) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<V>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedSubFastAsmOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr V Do(T, U) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<V>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedMulFastAsmOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr V Do(T, U) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<V>();
|
||||
}
|
||||
};
|
||||
#endif // BASE_HAS_ASSEMBLER_SAFE_MATH
|
||||
#undef BASE_HAS_ASSEMBLER_SAFE_MATH
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedAddFastOp {
|
||||
static const bool is_supported = true;
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
|
||||
return !__builtin_add_overflow(x, y, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedSubFastOp {
|
||||
static const bool is_supported = true;
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
|
||||
return !__builtin_sub_overflow(x, y, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedMulFastOp {
|
||||
#if defined(__clang__)
|
||||
// TODO(jschuh): Get the Clang runtime library issues sorted out so we can
|
||||
// support full-width, mixed-sign multiply builtins.
|
||||
// https://crbug.com/613003
|
||||
// We can support intptr_t, uintptr_t, or a smaller common type.
|
||||
static const bool is_supported =
|
||||
(IsTypeInRangeForNumericType<intptr_t, T>::value &&
|
||||
IsTypeInRangeForNumericType<intptr_t, U>::value) ||
|
||||
(IsTypeInRangeForNumericType<uintptr_t, T>::value &&
|
||||
IsTypeInRangeForNumericType<uintptr_t, U>::value);
|
||||
#else
|
||||
static const bool is_supported = true;
|
||||
#endif
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
|
||||
return CheckedMulFastAsmOp<T, U>::is_supported
|
||||
? CheckedMulFastAsmOp<T, U>::Do(x, y, result)
|
||||
: !__builtin_mul_overflow(x, y, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedAddFastOp {
|
||||
static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||
return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedSubFastOp {
|
||||
static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||
return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedMulFastOp {
|
||||
static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;
|
||||
template <typename V>
|
||||
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||
return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ClampedNegFastOp {
|
||||
static const bool is_supported = std::is_signed<T>::value;
|
||||
__attribute__((always_inline)) static T Do(T value) {
|
||||
// Use this when there is no assembler path available.
|
||||
if (!ClampedSubFastAsmOp<T, T>::is_supported) {
|
||||
T result;
|
||||
return !__builtin_sub_overflow(T(0), value, &result)
|
||||
? result
|
||||
: std::numeric_limits<T>::max();
|
||||
}
|
||||
|
||||
// Fallback to the normal subtraction path.
|
||||
return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
|
||||
240
oss/chromium/base/numerics/safe_math_shared_impl.h
Normal file
240
oss/chromium/base/numerics/safe_math_shared_impl.h
Normal file
@@ -0,0 +1,240 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
|
||||
#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
|
||||
#ifdef __asmjs__
|
||||
// Optimized safe math instructions are incompatible with asmjs.
|
||||
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
|
||||
// Where available use builtin math overflow support on Clang and GCC.
|
||||
#elif !defined(__native_client__) && \
|
||||
((defined(__clang__) && \
|
||||
((__clang_major__ > 3) || \
|
||||
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \
|
||||
(defined(__GNUC__) && __GNUC__ >= 5))
|
||||
#include "base/numerics/safe_math_clang_gcc_impl.h"
|
||||
#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
|
||||
#else
|
||||
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
// These are the non-functioning boilerplate implementations of the optimized
|
||||
// safe math routines.
|
||||
#if !BASE_HAS_OPTIMIZED_SAFE_MATH
|
||||
template <typename T, typename U>
|
||||
struct CheckedAddFastOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T, U, V*) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<bool>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedSubFastOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T, U, V*) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<bool>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct CheckedMulFastOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr bool Do(T, U, V*) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<bool>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedAddFastOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr V Do(T, U) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<V>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedSubFastOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr V Do(T, U) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<V>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct ClampedMulFastOp {
|
||||
static const bool is_supported = false;
|
||||
template <typename V>
|
||||
static constexpr V Do(T, U) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<V>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ClampedNegFastOp {
|
||||
static const bool is_supported = false;
|
||||
static constexpr T Do(T) {
|
||||
// Force a compile failure if instantiated.
|
||||
return CheckOnFailure::template HandleFailure<T>();
|
||||
}
|
||||
};
|
||||
#endif // BASE_HAS_OPTIMIZED_SAFE_MATH
|
||||
#undef BASE_HAS_OPTIMIZED_SAFE_MATH
|
||||
|
||||
// This is used for UnsignedAbs, where we need to support floating-point
|
||||
// template instantiations even though we don't actually support the operations.
|
||||
// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
|
||||
// so the float versions will not compile.
|
||||
template <typename Numeric,
|
||||
bool IsInteger = std::is_integral<Numeric>::value,
|
||||
bool IsFloat = std::is_floating_point<Numeric>::value>
|
||||
struct UnsignedOrFloatForSize;
|
||||
|
||||
template <typename Numeric>
|
||||
struct UnsignedOrFloatForSize<Numeric, true, false> {
|
||||
using type = typename std::make_unsigned<Numeric>::type;
|
||||
};
|
||||
|
||||
template <typename Numeric>
|
||||
struct UnsignedOrFloatForSize<Numeric, false, true> {
|
||||
using type = Numeric;
|
||||
};
|
||||
|
||||
// Wrap the unary operations to allow SFINAE when instantiating integrals versus
|
||||
// floating points. These don't perform any overflow checking. Rather, they
|
||||
// exhibit well-defined overflow semantics and rely on the caller to detect
|
||||
// if an overflow occured.
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
constexpr T NegateWrapper(T value) {
|
||||
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||
// This will compile to a NEG on Intel, and is normal negation on ARM.
|
||||
return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
constexpr T NegateWrapper(T value) {
|
||||
return -value;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
|
||||
return ~value;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
constexpr T AbsWrapper(T value) {
|
||||
return static_cast<T>(SafeUnsignedAbs(value));
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
constexpr T AbsWrapper(T value) {
|
||||
return value < 0 ? -value : value;
|
||||
}
|
||||
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R>
|
||||
struct MathWrapper {
|
||||
using math = M<typename UnderlyingType<L>::type,
|
||||
typename UnderlyingType<R>::type,
|
||||
void>;
|
||||
using type = typename math::result_type;
|
||||
};
|
||||
|
||||
// These variadic templates work out the return types.
|
||||
// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R,
|
||||
typename... Args>
|
||||
struct ResultType;
|
||||
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R>
|
||||
struct ResultType<M, L, R> {
|
||||
using type = typename MathWrapper<M, L, R>::type;
|
||||
};
|
||||
|
||||
template <template <typename, typename, typename> class M,
|
||||
typename L,
|
||||
typename R,
|
||||
typename... Args>
|
||||
struct ResultType {
|
||||
using type =
|
||||
typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
|
||||
};
|
||||
|
||||
// The following macros are just boilerplate for the standard arithmetic
|
||||
// operator overloads and variadic function templates. A macro isn't the nicest
|
||||
// solution, but it beats rewriting these over and over again.
|
||||
#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \
|
||||
template <typename L, typename R, typename... Args> \
|
||||
constexpr CLASS##Numeric< \
|
||||
typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
|
||||
CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
|
||||
return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
|
||||
args...); \
|
||||
}
|
||||
|
||||
#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
|
||||
/* Binary arithmetic operator for all CLASS##Numeric operations. */ \
|
||||
template <typename L, typename R, \
|
||||
typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \
|
||||
nullptr> \
|
||||
constexpr CLASS##Numeric< \
|
||||
typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
|
||||
operator OP(const L lhs, const R rhs) { \
|
||||
return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
|
||||
rhs); \
|
||||
} \
|
||||
/* Assignment arithmetic operator implementation from CLASS##Numeric. */ \
|
||||
template <typename L> \
|
||||
template <typename R> \
|
||||
constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \
|
||||
const R rhs) { \
|
||||
return MathOp<CLASS##OP_NAME##Op>(rhs); \
|
||||
} \
|
||||
/* Variadic arithmetic functions that return CLASS##Numeric. */ \
|
||||
BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
|
||||
15
oss/chromium/cgmanifest.json
Normal file
15
oss/chromium/cgmanifest.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/component-detection-manifest.json",
|
||||
"Registrations": [
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/chromium/chromium",
|
||||
"commitHash": "d8710dd959da8e3be56f20af8cc94fbf560fbb6b"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
@@ -135,17 +135,5 @@
|
||||
</Target>
|
||||
<!-- **END VC LIBS HACK** -->
|
||||
|
||||
<!-- This is required to get the package dependency in the AppXManifest. -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
|
||||
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
@@ -156,14 +157,6 @@
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
By default, the PRI file will contain resource paths beginning with the
|
||||
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
<!-- ========================= XAML files ======================== -->
|
||||
@@ -90,14 +91,6 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalThemeHelpers>true</TerminalThemeHelpers>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
@@ -142,14 +141,6 @@
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!-- Override GetPackagingOutputs to roll up all our dependencies.
|
||||
This ensures that when the WAP packaging project asks what files go into
|
||||
the package, we tell it.
|
||||
@@ -225,4 +216,3 @@
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
</Project>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -7,11 +7,27 @@
|
||||
#include "Row.hpp"
|
||||
#include "textBuffer.hpp"
|
||||
|
||||
static std::atomic<uint64_t> s_revision{ 0 };
|
||||
|
||||
ImageSlice::ImageSlice(const til::size cellSize) noexcept :
|
||||
_cellSize{ cellSize }
|
||||
{
|
||||
}
|
||||
|
||||
void ImageSlice::BumpRevision() noexcept
|
||||
{
|
||||
// Avoid setting the revision to 0. This allows the renderer to use 0 as a sentinel value.
|
||||
do
|
||||
{
|
||||
_revision = s_revision.fetch_add(1, std::memory_order_relaxed);
|
||||
} while (_revision == 0);
|
||||
}
|
||||
|
||||
uint64_t ImageSlice::Revision() const noexcept
|
||||
{
|
||||
return _revision;
|
||||
}
|
||||
|
||||
til::size ImageSlice::CellSize() const noexcept
|
||||
{
|
||||
return _cellSize;
|
||||
@@ -49,7 +65,6 @@ RGBQUAD* ImageSlice::MutablePixels(const til::CoordType columnBegin, const til::
|
||||
_columnBegin = existingData ? std::min(_columnBegin, columnBegin) : columnBegin;
|
||||
_columnEnd = existingData ? std::max(_columnEnd, columnEnd) : columnEnd;
|
||||
_pixelWidth = (_columnEnd - _columnBegin) * _cellSize.width;
|
||||
_pixelWidth = (_pixelWidth + 3) & ~3; // Renderer needs this as a multiple of 4
|
||||
const auto bufferSize = _pixelWidth * _cellSize.height;
|
||||
if (existingData)
|
||||
{
|
||||
@@ -108,9 +123,8 @@ void ImageSlice::CopyBlock(const TextBuffer& srcBuffer, const til::rect srcRect,
|
||||
|
||||
void ImageSlice::CopyRow(const ROW& srcRow, ROW& dstRow)
|
||||
{
|
||||
const auto& srcSlice = srcRow.GetImageSlice();
|
||||
auto& dstSlice = dstRow.GetMutableImageSlice();
|
||||
dstSlice = srcSlice ? std::make_unique<ImageSlice>(*srcSlice) : nullptr;
|
||||
const auto srcSlice = srcRow.GetImageSlice();
|
||||
dstRow.SetImageSlice(srcSlice ? std::make_unique<ImageSlice>(*srcSlice) : nullptr);
|
||||
}
|
||||
|
||||
void ImageSlice::CopyCells(const ROW& srcRow, const til::CoordType srcColumn, ROW& dstRow, const til::CoordType dstColumnBegin, const til::CoordType dstColumnEnd)
|
||||
@@ -119,24 +133,25 @@ void ImageSlice::CopyCells(const ROW& srcRow, const til::CoordType srcColumn, RO
|
||||
// a blank image into the destination, which is the same thing as an erase.
|
||||
// Also if the line renditions are different, there's no meaningful way to
|
||||
// copy the image content, so we also just treat that as an erase.
|
||||
const auto& srcSlice = srcRow.GetImageSlice();
|
||||
const auto srcSlice = srcRow.GetImageSlice();
|
||||
if (!srcSlice || srcRow.GetLineRendition() != dstRow.GetLineRendition()) [[likely]]
|
||||
{
|
||||
ImageSlice::EraseCells(dstRow, dstColumnBegin, dstColumnEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& dstSlice = dstRow.GetMutableImageSlice();
|
||||
auto dstSlice = dstRow.GetMutableImageSlice();
|
||||
if (!dstSlice)
|
||||
{
|
||||
dstSlice = std::make_unique<ImageSlice>(srcSlice->CellSize());
|
||||
dstSlice = dstRow.SetImageSlice(std::make_unique<ImageSlice>(srcSlice->CellSize()));
|
||||
__assume(dstSlice != nullptr);
|
||||
}
|
||||
const auto scale = srcRow.GetLineRendition() != LineRendition::SingleWidth ? 1 : 0;
|
||||
if (dstSlice->_copyCells(*srcSlice, srcColumn << scale, dstColumnBegin << scale, dstColumnEnd << scale))
|
||||
{
|
||||
// If _copyCells returns true, that means the destination was
|
||||
// completely erased, so we can delete this slice.
|
||||
dstSlice = nullptr;
|
||||
dstRow.SetImageSlice(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +218,7 @@ void ImageSlice::EraseCells(TextBuffer& buffer, const til::point at, const size_
|
||||
|
||||
void ImageSlice::EraseCells(ROW& row, const til::CoordType columnBegin, const til::CoordType columnEnd)
|
||||
{
|
||||
auto& imageSlice = row.GetMutableImageSlice();
|
||||
const auto imageSlice = row.GetMutableImageSlice();
|
||||
if (imageSlice) [[unlikely]]
|
||||
{
|
||||
const auto scale = row.GetLineRendition() != LineRendition::SingleWidth ? 1 : 0;
|
||||
@@ -211,7 +226,7 @@ void ImageSlice::EraseCells(ROW& row, const til::CoordType columnBegin, const ti
|
||||
{
|
||||
// If _eraseCells returns true, that means the image was
|
||||
// completely erased, so we can delete this slice.
|
||||
imageSlice = nullptr;
|
||||
row.SetImageSlice(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public:
|
||||
ImageSlice(const ImageSlice& rhs) = default;
|
||||
ImageSlice(const til::size cellSize) noexcept;
|
||||
|
||||
void BumpRevision() noexcept;
|
||||
uint64_t Revision() const noexcept;
|
||||
|
||||
til::size CellSize() const noexcept;
|
||||
til::CoordType ColumnOffset() const noexcept;
|
||||
til::CoordType PixelWidth() const noexcept;
|
||||
@@ -45,6 +48,7 @@ private:
|
||||
bool _copyCells(const ImageSlice& srcSlice, const til::CoordType srcColumn, const til::CoordType dstColumnBegin, const til::CoordType dstColumnEnd);
|
||||
bool _eraseCells(const til::CoordType columnBegin, const til::CoordType columnEnd);
|
||||
|
||||
uint64_t _revision = 0;
|
||||
til::size _cellSize;
|
||||
std::vector<RGBQUAD> _pixelBuffer;
|
||||
til::CoordType _columnBegin = 0;
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include "../../types/inc/convert.hpp"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
|
||||
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
|
||||
// initializer list leads to this warning. Can probably be removed in the future.
|
||||
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
|
||||
|
||||
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR };
|
||||
|
||||
OutputCell::OutputCell() noexcept :
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
|
||||
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
|
||||
// initializer list leads to this warning. Can probably be removed in the future.
|
||||
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
|
||||
|
||||
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR, INVALID_COLOR };
|
||||
|
||||
// Routine Description:
|
||||
@@ -274,7 +278,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
}
|
||||
case Mode::CharInfo:
|
||||
{
|
||||
// Walk forward by one because charinfos are just the legacy version of cells and prealigned to columns
|
||||
// Walk forward by one because char infos are just the legacy version of cells and prealigned to columns
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#include "OutputCellView.hpp"
|
||||
|
||||
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
|
||||
// initializer list leads to this warning. Can probably be removed in the future.
|
||||
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
|
||||
|
||||
// Routine Description:
|
||||
// - Constructs a read-only view of data formatted as a single output buffer cell
|
||||
// Arguments:
|
||||
|
||||
@@ -162,7 +162,7 @@ til::CoordType CharToColumnMapper::GetTrailingColumnAt(const wchar_t* str) noexc
|
||||
ROW::ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute) :
|
||||
_charsBuffer{ charsBuffer },
|
||||
_chars{ charsBuffer, rowWidth },
|
||||
_charOffsets{ charOffsetsBuffer, gsl::narrow_cast<size_t>(rowWidth) + 1u },
|
||||
_charOffsets{ charOffsetsBuffer, ::base::strict_cast<size_t>(rowWidth) + 1u },
|
||||
_attr{ rowWidth, fillAttribute },
|
||||
_columnCount{ rowWidth }
|
||||
{
|
||||
@@ -965,14 +965,26 @@ std::vector<uint16_t> ROW::GetHyperlinks() const
|
||||
return ids;
|
||||
}
|
||||
|
||||
const ImageSlice::Pointer& ROW::GetImageSlice() const noexcept
|
||||
ImageSlice* ROW::SetImageSlice(ImageSlice::Pointer imageSlice) noexcept
|
||||
{
|
||||
return _imageSlice;
|
||||
_imageSlice = std::move(imageSlice);
|
||||
return GetMutableImageSlice();
|
||||
}
|
||||
|
||||
ImageSlice::Pointer& ROW::GetMutableImageSlice() noexcept
|
||||
const ImageSlice* ROW::GetImageSlice() const noexcept
|
||||
{
|
||||
return _imageSlice;
|
||||
return _imageSlice.get();
|
||||
}
|
||||
|
||||
ImageSlice* ROW::GetMutableImageSlice() noexcept
|
||||
{
|
||||
const auto ptr = _imageSlice.get();
|
||||
if (!ptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
ptr->BumpRevision();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint16_t ROW::size() const noexcept
|
||||
@@ -1088,7 +1100,7 @@ DbcsAttribute ROW::DbcsAttrAt(til::CoordType column) const noexcept
|
||||
attr = DbcsAttribute::Trailing;
|
||||
}
|
||||
// Safety: col+1 is [1, _columnCount].
|
||||
else if (_uncheckedIsTrailer(gsl::narrow_cast<size_t>(col) + 1u))
|
||||
else if (_uncheckedIsTrailer(::base::strict_cast<size_t>(col) + 1u))
|
||||
{
|
||||
attr = DbcsAttribute::Leading;
|
||||
}
|
||||
|
||||
@@ -152,8 +152,9 @@ public:
|
||||
const til::small_rle<TextAttribute, uint16_t, 1>& Attributes() const noexcept;
|
||||
TextAttribute GetAttrByColumn(til::CoordType column) const;
|
||||
std::vector<uint16_t> GetHyperlinks() const;
|
||||
const ImageSlice::Pointer& GetImageSlice() const noexcept;
|
||||
ImageSlice::Pointer& GetMutableImageSlice() noexcept;
|
||||
ImageSlice* SetImageSlice(ImageSlice::Pointer imageSlice) noexcept;
|
||||
const ImageSlice* GetImageSlice() const noexcept;
|
||||
ImageSlice* GetMutableImageSlice() noexcept;
|
||||
uint16_t size() const noexcept;
|
||||
til::CoordType GetLastNonSpaceColumn() const noexcept;
|
||||
til::CoordType MeasureLeft() const noexcept;
|
||||
@@ -299,8 +300,6 @@ private:
|
||||
til::small_rle<TextAttribute, uint16_t, 1> _attr;
|
||||
// The width of the row in visual columns.
|
||||
uint16_t _columnCount = 0;
|
||||
// Stores any image content covering the row.
|
||||
ImageSlice::Pointer _imageSlice;
|
||||
// Stores double-width/height (DECSWL/DECDWL/DECDHL) attributes.
|
||||
LineRendition _lineRendition = LineRendition::SingleWidth;
|
||||
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line
|
||||
@@ -309,6 +308,9 @@ private:
|
||||
bool _doubleBytePadded = false;
|
||||
|
||||
std::optional<ScrollbarData> _promptData = std::nullopt;
|
||||
|
||||
// Stores any image content covering the row.
|
||||
ImageSlice::Pointer _imageSlice;
|
||||
};
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
|
||||
@@ -65,47 +65,6 @@ void TextAttribute::SetLegacyDefaultAttributes(const WORD defaultAttributes) noe
|
||||
gsl::at(s_legacyBackgroundColorMap, s_legacyDefaultBackground) = TextColor{};
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// Pursuant to GH#6807
|
||||
// This routine replaces VT colors from the 16-color set with the "default"
|
||||
// flag. It is intended to be used as part of the "VT Quirk" in
|
||||
// WriteConsole[AW].
|
||||
//
|
||||
// There is going to be a very long tail of applications that will
|
||||
// explicitly request VT SGR 40/37 when what they really want is to
|
||||
// SetConsoleTextAttribute() with a black background/white foreground.
|
||||
// Instead of making those applications look bad (and therefore making us
|
||||
// look bad, because we're releasing this as an update to something that
|
||||
// "looks good" already), we're introducing this compatibility hack. Before
|
||||
// the color reckoning in GH#6698 + GH#6506, *every* color was subject to
|
||||
// being spontaneously and erroneously turned into the default color. Now,
|
||||
// only the 16-color palette value that matches the active console
|
||||
// background color will be destroyed when the quirk is enabled.
|
||||
//
|
||||
// This is not intended to be a long-term solution. This comment will be
|
||||
// discovered in forty years(*) time and people will laugh at our hubris.
|
||||
//
|
||||
// *it doesn't matter when you're reading this, it will always be 40 years
|
||||
// from now.
|
||||
TextAttribute TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept
|
||||
{
|
||||
const auto fg{ attribute.GetForeground() };
|
||||
const auto bg{ attribute.GetBackground() };
|
||||
auto copy{ attribute };
|
||||
if (fg.IsIndex16() &&
|
||||
attribute.IsIntense() == WI_IsFlagSet(s_ansiDefaultForeground, FOREGROUND_INTENSITY) &&
|
||||
fg.GetIndex() == (s_ansiDefaultForeground & ~FOREGROUND_INTENSITY))
|
||||
{
|
||||
// We don't want to turn 1;37m into 39m (or even 1;39m), as this was meant to mimic a legacy color.
|
||||
copy.SetDefaultForeground();
|
||||
}
|
||||
if (bg.IsIndex16() && bg.GetIndex() == s_ansiDefaultBackground)
|
||||
{
|
||||
copy.SetDefaultBackground();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Returns a WORD with legacy-style attributes for this textattribute.
|
||||
// Parameters:
|
||||
|
||||
@@ -93,7 +93,6 @@ public:
|
||||
}
|
||||
|
||||
static void SetLegacyDefaultAttributes(const WORD defaultAttributes) noexcept;
|
||||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
bool IsTopHorizontalDisplayed() const noexcept;
|
||||
|
||||
@@ -82,7 +82,8 @@ public:
|
||||
static constexpr size_t FRAME_FOREGROUND = 263;
|
||||
static constexpr size_t FRAME_BACKGROUND = 264;
|
||||
static constexpr size_t CURSOR_COLOR = 265;
|
||||
static constexpr size_t TABLE_SIZE = 266;
|
||||
static constexpr size_t SELECTION_BACKGROUND = 266;
|
||||
static constexpr size_t TABLE_SIZE = 267;
|
||||
|
||||
constexpr TextColor() noexcept :
|
||||
_meta{ ColorType::IsDefault },
|
||||
|
||||
@@ -16,7 +16,7 @@ bool Search::IsStale(const Microsoft::Console::Render::IRenderData& renderData,
|
||||
_lastMutationId != renderData.GetTextBuffer().GetLastMutationId();
|
||||
}
|
||||
|
||||
bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse)
|
||||
void Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse)
|
||||
{
|
||||
const auto& textBuffer = renderData.GetTextBuffer();
|
||||
|
||||
@@ -30,15 +30,15 @@ bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const st
|
||||
_results = std::move(result).value_or(std::vector<til::point_span>{});
|
||||
_index = reverse ? gsl::narrow_cast<ptrdiff_t>(_results.size()) - 1 : 0;
|
||||
_step = reverse ? -1 : 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Search::MoveToCurrentSelection()
|
||||
{
|
||||
if (_renderData->IsSelectionActive())
|
||||
{
|
||||
MoveToPoint(_renderData->GetTextBuffer().ScreenToBufferPosition(_renderData->GetSelectionAnchor()));
|
||||
}
|
||||
else if (const auto span = _renderData->GetSearchHighlightFocused())
|
||||
{
|
||||
MoveToPoint(_step > 0 ? span->start : span->end);
|
||||
}
|
||||
}
|
||||
|
||||
void Search::MoveToPoint(const til::point anchor) noexcept
|
||||
|
||||
@@ -36,9 +36,8 @@ public:
|
||||
Search() = default;
|
||||
|
||||
bool IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags) const noexcept;
|
||||
bool Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse);
|
||||
void Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse);
|
||||
|
||||
void MoveToCurrentSelection();
|
||||
void MoveToPoint(til::point anchor) noexcept;
|
||||
void MovePastPoint(til::point anchor) noexcept;
|
||||
void FindNext(bool reverse) noexcept;
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include "../types/inc/utils.hpp"
|
||||
#include "search.h"
|
||||
|
||||
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
|
||||
// initializer list leads to this warning. Can probably be removed in the future.
|
||||
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace Microsoft::Console::Types;
|
||||
|
||||
@@ -98,7 +102,7 @@ void TextBuffer::_reserve(til::size screenBufferSize, const TextAttribute& defau
|
||||
// 65535*65535 cells would result in a allocSize of 8GiB.
|
||||
// --> Use uint64_t so that we can safely do our calculations even on x86.
|
||||
// We allocate 1 additional row, which will be used for GetScratchpadRow().
|
||||
const auto rowCount = gsl::narrow_cast<uint64_t>(h) + 1;
|
||||
const auto rowCount = ::base::strict_cast<uint64_t>(h) + 1;
|
||||
const auto allocSize = gsl::narrow<size_t>(rowCount * rowStride);
|
||||
|
||||
// NOTE: Modifications to this block of code might have to be mirrored over to ResizeTraditional().
|
||||
@@ -447,14 +451,9 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord
|
||||
cwd.GraphemeNext(state, chars);
|
||||
col += state.width;
|
||||
|
||||
// If we ran out of columns, we need to always return `columnLimit` and not `cols`,
|
||||
// because if we tried inserting a wide glyph into just 1 remaining column it will
|
||||
// fail to fit, but that remaining column still has been used up. When the caller sees
|
||||
// `columns == columnLimit` they will line-wrap and continue inserting into the next row.
|
||||
if (col > columnLimit)
|
||||
{
|
||||
columns = columnLimit;
|
||||
return dist;
|
||||
break;
|
||||
}
|
||||
|
||||
dist += state.len;
|
||||
@@ -462,7 +461,7 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord
|
||||
|
||||
// But if we simply ran out of text we just need to return the actual number of columns.
|
||||
columns = col;
|
||||
return chars.size();
|
||||
return dist;
|
||||
}
|
||||
|
||||
// Pretend as if `position` is a regular cursor in the TextBuffer.
|
||||
@@ -911,7 +910,7 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition, cons
|
||||
// If the line rendition has changed, the row can no longer be wrapped.
|
||||
row.SetWrapForced(false);
|
||||
// And all image content on the row is removed.
|
||||
row.GetMutableImageSlice().reset();
|
||||
row.SetImageSlice(nullptr);
|
||||
// And if it's no longer single width, the right half of the row should be erased.
|
||||
if (lineRendition != LineRendition::SingleWidth)
|
||||
{
|
||||
@@ -2848,6 +2847,15 @@ void TextBuffer::Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const View
|
||||
}
|
||||
}
|
||||
|
||||
// The for loop right after this if condition will copy entire rows of attributes at a time.
|
||||
// This assumes of course that the "write cursor" (newX, newY) is at the start of a row.
|
||||
// If we didn't check for this, we may otherwise copy attributes from a later row into a previous one.
|
||||
if (newX != 0)
|
||||
{
|
||||
newX = 0;
|
||||
newY++;
|
||||
}
|
||||
|
||||
// Finish copying buffer attributes to remaining rows below the last
|
||||
// printable character. This is to fix the `color 2f` scenario, where you
|
||||
// change the buffer colors then resize and everything below the last
|
||||
@@ -3253,23 +3261,30 @@ MarkExtents TextBuffer::_scrollMarkExtentForRow(const til::CoordType rowOffset,
|
||||
return mark;
|
||||
}
|
||||
|
||||
std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const
|
||||
std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset,
|
||||
const til::CoordType bottomInclusive,
|
||||
const bool clipAtCursor) const
|
||||
{
|
||||
std::wstring commandBuilder;
|
||||
MarkKind lastMarkKind = MarkKind::Prompt;
|
||||
const auto cursorPosition = GetCursor().GetPosition();
|
||||
for (auto y = rowOffset; y <= bottomInclusive; y++)
|
||||
{
|
||||
const bool onCursorRow = clipAtCursor && y == cursorPosition.y;
|
||||
// Now we need to iterate over text attributes. We need to find a
|
||||
// segment of Prompt attributes, we'll skip those. Then there should be
|
||||
// Command attributes. Collect up all of those, till we get to the next
|
||||
// Output attribute.
|
||||
|
||||
const auto& row = GetRowByOffset(y);
|
||||
const auto runs = row.Attributes().runs();
|
||||
auto x = 0;
|
||||
for (const auto& [attr, length] : runs)
|
||||
{
|
||||
const auto nextX = gsl::narrow_cast<uint16_t>(x + length);
|
||||
auto nextX = gsl::narrow_cast<uint16_t>(x + length);
|
||||
if (onCursorRow)
|
||||
{
|
||||
nextX = std::min(nextX, gsl::narrow_cast<uint16_t>(cursorPosition.x));
|
||||
}
|
||||
const auto markKind{ attr.GetMarkAttributes() };
|
||||
if (markKind != lastMarkKind)
|
||||
{
|
||||
@@ -3289,6 +3304,10 @@ std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset, const ti
|
||||
}
|
||||
// advance to next run of text
|
||||
x = nextX;
|
||||
if (onCursorRow && x == cursorPosition.x)
|
||||
{
|
||||
return commandBuilder;
|
||||
}
|
||||
}
|
||||
// we went over all the runs in this row, but we're not done yet. Keep iterating on the next row.
|
||||
}
|
||||
@@ -3312,7 +3331,7 @@ std::wstring TextBuffer::CurrentCommand() const
|
||||
// This row did start a prompt! Find the prompt that starts here.
|
||||
// Presumably, no rows below us will have prompts, so pass in the last
|
||||
// row with text as the bottom
|
||||
return _commandForRow(promptY, _estimateOffsetOfLastCommittedRow());
|
||||
return _commandForRow(promptY, _estimateOffsetOfLastCommittedRow(), true);
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ private:
|
||||
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
void _PruneHyperlinks();
|
||||
|
||||
std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const;
|
||||
std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive, const bool clipAtCursor = false) const;
|
||||
MarkExtents _scrollMarkExtentForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const;
|
||||
bool _createPromptMarkIfNeeded();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user